# X = X1
# Y = Y4
x <- c(9.3, 4.1, 22.4, 9.1, 15.8, 7.1, 15.9, 6.9, 16.0, 6.7, 8.2, 16.0, 6.4, 11.8, 3.5, 21.7, 12.2, 9.3, 8.0, 6.2)
y <- c(20.2, 18.6, 22.6, 11.4, 23.6, 24.0, 26.0, 26.8, 19.7, 22.7, 16.8, 20.2, 21.7, 20.9, 26.9, 16.3, 19.9, 15.5, 26.5, 21.7)
summary.x <- summary(x)
summary.y <- summary(y)
lil.x <- summary.x[5]
lil.y <- summary.y[2]
plot(x,y)
abline(lm(y~x))

Problem 1

Probability

Conditional probability is: \[ P(A|B) = \frac{P(A \cap B)}{P(B)} \] First, I defined A, B, and \(A \cap B\) for each case asked. I used the which function to fine which entries met the criteria in each case. Then I used the length function to count those. I then divided by the total length of the vector to get a percentage (expressed as a decimal between 0 and 1).

# P(X>x)
p1 <- length(which(x>lil.x))/length(x)
# P(Y>y)
p2 <- length(which(y>lil.y))/length(y)
# P(X<x)
p3 <- length(which(x<lil.x))/length(x)
# P(X>x & Y>y)
p4 <- length(which(x>lil.x & y > lil.y))/length(x)
# P(X<x & Y>y)
p5 <- length(which(x<lil.x & y >lil.y))/length(x)
# P(X<x & Y<y)
p6 <- length(which(x<lil.x & y <lil.y))/length(x)
# P(X>x & Y <y)
p7 <- length(which(x>lil.x & y <lil.y))/length(x)

P(X>x| Y>y)

\[ P(X>x | Y>y) = \frac{P(X>x \cap Y>y)}{P(Y>y)} \]

pa.and.pb <- p4
pb <- p2
result <- pa.and.pb/pb
result
[1] 0.2666667

There is a 26% chance of X>x when Y>y.

P(X>x, Y>y)

\[ P(X>x, Y>y) = P(X>x \cap Y>y) \]

p4
[1] 0.2

There is a 20% chance of X>x and Y>y.

P(X < x | Y > y)

\[ P(X>x | Y>y) = \frac{P(X<x \cap Y>y)}{P(Y>y)} \]

pa.and.pb <- p5
pb <- p2
result <- pa.and.pb/pb
result
[1] 0.7333333

There is a a 73% chance of \(X < x\) given \(Y > y\).

Table of Counts

table <- matrix(c(p6, p7,p5, p4), ncol = 2) *length(x)
table2 <- t(matrix(c(4,11, 15, 1, 4, 5, 5, 15, 20), ncol= 3))
table2
     [,1] [,2] [,3]
[1,]    4   11   15
[2,]    1    4    5
[3,]    5   15   20

Independence

If splitting the training data in this way made them independent, it would be true that \[ P(AB) = P(A)P(B) \] where \[ A = [X> x] \\ B = [Y>y] \\ AB = [X>x \cap Y>y] \]

A <- p1
B <- p2
AB <- p4
A*B == AB
[1] FALSE

These two data points are not independent. Below we can verify that with a chi sq test on the table above (minus the total rows/columns)

c1 <- rbind(4,1)
c2 <- rbind(11,4)
mtx <- cbind(c1,c2)
mtx
     [,1] [,2]
[1,]    4   11
[2,]    1    4
chisq.test(mtx)
Chi-squared approximation may be incorrect

    Pearson's Chi-squared test with Yates' continuity
    correction

data:  mtx
X-squared = 0, df = 1, p-value = 1

This throws a warning becauase some of the cells are <5. Instead, we can do a Fisher’s Exact Test.

fisher.test(c(p6,p7), c(p5,p4))

    Fisher's Exact Test for Count Data

data:  c(p6, p7) and c(p5, p4)
p-value = 1
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
 0.02564066        Inf
sample estimates:
odds ratio 
       Inf 

Problem 2

Descriptive and Inferential Statistics

df <- as.data.frame(read.csv("data/train.csv"))
summary(df$SalePrice)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  34900  129975  163000  180921  214000  755000 
hist(df$SalePrice)

summary(df$GrLivArea)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    334    1130    1464    1515    1777    5642 
hist(df$GrLivArea)

summary(df$YearBuilt)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1872    1954    1973    1971    2000    2010 
hist(df$YearBuilt)

summary(df$LotArea)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1300    7554    9478   10517   11602  215245 
hist(df$LotArea, breaks = 100)

plot(df$SalePrice ~ df$GrLivArea)

plot(df$SalePrice ~ df$YearBuilt)

plot(df$SalePrice ~ df$LotArea)

df2 <- cbind(df$GrLivArea, df$YearBuilt, df$LotArea)
correlation.matrix <- cor(df2)
correlation.matrix
          [,1]       [,2]       [,3]
[1,] 1.0000000 0.19900971 0.26311617
[2,] 0.1990097 1.00000000 0.01422765
[3,] 0.2631162 0.01422765 1.00000000
col.1 <- df$GrLivArea
col.2 <- df$YearBuilt
col.3 <- df$LotArea
test1 <- cor.test(col.1, col.2, conf.level = .8)
conf.inv.1 <- test1$conf.int
test2 <- cor.test(col.1, col.3, conf.level = .8)
conf.inv.2 <- test2$conf.int
test3 <- cor.test(col.2, col.3, conf.level = .8)
conf.inv.3 <- test3$conf.int

Hypothesis test:

\(H_0\): The correlation between each variable pair is 0. \(H_A\): The correlation between is variable pair is not 0.

conf.inv.1
[1] 0.1665605 0.2310283
attr(,"conf.level")
[1] 0.8
conf.inv.2
[1] 0.2315997 0.2940809
attr(,"conf.level")
[1] 0.8
conf.inv.3
[1] -0.01934322  0.04776648
attr(,"conf.level")
[1] 0.8

Analysis

When \(\alpha = .2\), we find that living area and year built are seemingly correlated. Additionally, we find that the living area and lot area are correlated. However, a correlation of 0 is within our confidence interval when comparing lot size to year built. Additionally, the family-wise error rate is

FWE <- 1-(1-.2)^3
FWE
[1] 0.488

which means there is a 49% chance of a Type 1 Error. This is very worrying considering we only did 3 tests.

Linear Algebra and Correlation

Invert your 3 x 3 correlation matrix from above. (This is known as the precision matrix and contains variance inflation factors on the diagonal.) Multiply the correlation matrix by the precision matrix, and then multiply the precision matrix by the correlation matrix. Conduct LU decomposition on the matrix.

The inverse exists if and only if the determinant is not 0. In this cases, the determinant = .89. So, an inverse exists.

det(correlation.matrix)
[1] 0.8924526

An inverse of a matrix is defined by \[ A A^{-1} = I = A^{-1} A \] The mechanics of this calculation involve finding a determinant, an adjugate, and multiplying them together. However, R gives us some handy tools for doing matrix math. Below, I use the matlib library to find an inverse.

library(matlib)
precision.matrix <- solve(correlation.matrix)
mtx1 <- correlation.matrix %*% precision.matrix
mtx2 <- precision.matrix %*% correlation.matrix
mtx1
             [,1]         [,2]          [,3]
[1,] 1.000000e+00 1.387779e-17  0.000000e+00
[2,] 7.806256e-18 1.000000e+00 -1.734723e-18
[3,] 0.000000e+00 6.938894e-18  1.000000e+00
mtx2
              [,1]          [,2]          [,3]
[1,]  1.000000e+00  3.642919e-17  5.551115e-17
[2,] -1.561251e-17  1.000000e+00 -6.938894e-18
[3,] -5.551115e-17 -1.734723e-18  1.000000e+00

LU decomposition allows for a quick recalculation if the right hand side of our equation changes (by, say adding more data). This allows us to quickly compute the new values for the \(\overline{b}\). Below I use the lu_factorization function I wrote all the way back in assignment 1.

lu_factorization <- function(A){
  library(matrixcalc)
  if (nrow(A)!= ncol(A)){
    stop("Must be a square matrix!")
  }
  size <- nrow(A)
  L <- matrix (0, nrow = size, ncol = size)
  U <- matrix (0, nrow = size, ncol = size)
  for (i in 1:size){
    L[i,i] = 1
  }
  for ( i in 1:size ) {
        up <- i + 1
        down <- i - 1
        for ( j in 1:size ) {
            U[i,j] <- A[i,j]
            if ( down > 0 ) {
                for ( k in 1:down ) {
                    U[i,j] <- U[i,j] - L[i,k] * U[k,j]
                }
            }
        }
        if ( up <= size ) {
            for ( j in up:size ) {
                L[j,i] <- A[j,i]
                if ( down > 0 ) {
                    for ( k in 1:down ) {
                        L[j,i] <- L[j,i] - L[j,k] * U[k,i]
                    }
                }
                L[j,i] <- L[j,i] / U[i,i]
            }    
        }
  }
  answer <- list(L,U)
  return(answer)
}
L <- lu_factorization(correlation.matrix)[1]
U <- lu_factorization(correlation.matrix)[2]
L <- as.data.frame(L)
U <- as.data.frame(U)
L <- as.matrix(L)
U <- as.matrix(U)
L
            X1          X2 X3
[1,] 1.0000000  0.00000000  0
[2,] 0.1990097  1.00000000  0
[3,] 0.2631162 -0.03970764  1
U
     X1        X2          X3
[1,]  1 0.1990097  0.26311617
[2,]  0 0.9603951 -0.03813502
[3,]  0 0.0000000  0.92925563
L %*% U
            X1         X2         X3
[1,] 1.0000000 0.19900971 0.26311617
[2,] 0.1990097 1.00000000 0.01422765
[3,] 0.2631162 0.01422765 1.00000000

Because LU decomposition is not unique, I cannot check it directly. However, I can make sure that L * U results in the same answer whether I use my function or the one from pracma.

library(pracma)
res <- lu(correlation.matrix)
res$L %*% res$U
          [,1]       [,2]       [,3]
[1,] 1.0000000 0.19900971 0.26311617
[2,] 0.1990097 1.00000000 0.01422765
[3,] 0.2631162 0.01422765 1.00000000

Calculus-Based Probability and Statistics

Find the optimal value of \(\lambda\) for this distribution, and then take 1000 samples from this exponential distribution using this value (e.g., rexp(1000, \(\lambda\)). Plot a histogram and compare it with a histogram of your original variable. Using the exponential pdf, find the 5th and 95th percentiles using the cumulative distribution function (CDF). Also generate a 95% confidence interval from the empirical data, assuming normality. Finally, provide the empirical 5th percentile and 95th percentile of the data. Discuss.

library(MASS)
attach(data)
The following objects are masked from data (pos = 3):

    Condition, GarageSize, Price, Quality, Rooms,
    SaleYear, Size

The following objects are masked from data (pos = 4):

    Condition, GarageSize, Price, Quality, Rooms,
    SaleYear, Size

The following objects are masked from data (pos = 5):

    Condition, GarageSize, Price, Quality, Rooms,
    SaleYear, Size

The following objects are masked from data (pos = 6):

    Condition, GarageSize, Price, Quality, Rooms,
    SaleYear, Size

The following objects are masked from data (pos = 7):

    Condition, GarageSize, Price, Quality, Rooms,
    SaleYear, Size
par(mfrow = c(1,2))
hist(df$GrLivArea)
model2 <- fitdistr(x = df$GrLivArea, "exponential")
ex <- rexp(1000, rate = model2$estimate)
hist(ex)

lower <- qexp(p = .05, rate = model2$estimate)
upper <- qexp(p = .95, rate = model2$estimate)
range1 <- c(lower,upper)
range1
[1]   77.73313 4539.92351

The above show the 5 and 95 percentiles for the fitted exponential function.

upper <- mean(df$GrLivArea) + sd(df$GrLivArea)*1.65
lower <- mean(df$GrLivArea) - sd(df$GrLivArea)*1.65
range2 <- c(lower, upper)
range2
[1]  648.4211 2382.5063

The above shows the 5 and 95 percentiles for a normally distributed function with the same mean and standard deviation as our original data.

lower <- quantile(df$GrLivArea, .05)
upper <- quantile(df$GrLivArea, .95)
range3 <- c(lower, upper)
range3
    5%    95% 
 848.0 2466.1 

By looking at the residuals of the quantiles (as well as the initial histograms), we see that our data is much more suited to a normal distributution than an exponential one.

Multiple Linear Regression

Below, I made a model attempted to measure sale price from Overall Quality, Graded Living Area, Sale Year and Overall Condition. Below is are the summary plots for the model and its summary statistics.

data <- as.data.frame(cbind(df$OverallQual, df$GrLivArea, df$YearBuilt, df$OverallCond, df$SalePrice, df$BedroomAbvGr, df$GarageType))
colnames(data) <- c("Quality", "Size", "SaleYear",  "Condition", "Price", "Rooms", "GarageSize")
model3 <- lm(Price ~I(Quality**5) + I(Size**2) + SaleYear + Condition + Rooms, data = data)
plot(model3)

summary(model3)

Call:
lm(formula = Price ~ I(Quality^5) + I(Size^2) + SaleYear + Condition + 
    Rooms, data = data)

Residuals:
    Min      1Q  Median      3Q     Max 
-619774  -17314   -2676   13211  256827 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -1.443e+06  8.365e+04 -17.251   <2e-16 ***
I(Quality^5)  2.521e+00  9.194e-02  27.415   <2e-16 ***
I(Size^2)     1.220e-02  7.168e-04  17.016   <2e-16 ***
SaleYear      7.636e+02  4.141e+01  18.441   <2e-16 ***
Condition     8.383e+03  9.971e+02   8.407   <2e-16 ***
Rooms         2.398e+03  1.482e+03   1.618    0.106    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 39240 on 1454 degrees of freedom
Multiple R-squared:  0.7569,    Adjusted R-squared:  0.7561 
F-statistic: 905.5 on 5 and 1454 DF,  p-value: < 2.2e-16

This model does a decent job modelling house prices, except for the most luxurious of houses. The residuals appear to be relatively normally distributed about the fitted values, except for the tails where we simply don’t have many data points. Additionally, the Normal Q-Q plot shows us that our model fits our data very neatly until the most luxurious of houses. This would suggest that these luxury homes have prices beyond size, quality, sale year, and condition. The other option is that these measures are not granular enough in our data to differentiate the most exquisitie houses. With an R^2 value of .75 and a relatively small number of features, I am satisfied with this model.

Below, I use the test data set to test my model. I then write it to a csv in the data folder called “kaggle_results.csv”.

df2<- read.csv("data/test.csv")
new.data <- as.data.frame(cbind(df2$OverallQual, df2$GrLivArea, df2$YearBuilt, df2$OverallCond, df2$BedroomAbvGr))
colnames(new.data) <- c("Quality", "Size", "SaleYear",  "Condition", "Rooms")
predictions <- as.data.frame(round(predict.lm(object = model3, newdata = new.data), 2))
index <- df2$Id
predictions <- as.data.frame(cbind(index, predictions))
colnames(predictions) <- c("ID", "SalePrice")
write.csv(predictions, file = 'data/kaggle_results.csv', row.names = FALSE)

After submitting the csv manually to Kaggle, I ranked 4612 under the handle @simplymathematics.

Video

https://www.useloom.com/share/a5cdeaf47e3a48e7bccbd17000bd3ed8

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CiMgWCA9IFgxCiMgWSA9IFk0Cgp4IDwtIGMoOS4zLCA0LjEsIDIyLjQsIDkuMSwgMTUuOCwgNy4xLCAxNS45LCA2LjksIDE2LjAsIDYuNywgOC4yLCAxNi4wLCA2LjQsIDExLjgsIDMuNSwgMjEuNywgMTIuMiwgOS4zLCA4LjAsIDYuMikKCnkgPC0gYygyMC4yLCAxOC42LCAyMi42LCAxMS40LCAyMy42LCAyNC4wLCAyNi4wLCAyNi44LCAxOS43LCAyMi43LCAxNi44LCAyMC4yLCAyMS43LCAyMC45LCAyNi45LCAxNi4zLCAxOS45LCAxNS41LCAyNi41LCAyMS43KQoKCnN1bW1hcnkueCA8LSBzdW1tYXJ5KHgpCnN1bW1hcnkueSA8LSBzdW1tYXJ5KHkpCmxpbC54IDwtIHN1bW1hcnkueFs1XQpsaWwueSA8LSBzdW1tYXJ5LnlbMl0KcGxvdCh4LHkpCmFibGluZShsbSh5fngpKQpgYGAKCiMgUHJvYmxlbSAxCgojIyBQcm9iYWJpbGl0eQoKQ29uZGl0aW9uYWwgcHJvYmFiaWxpdHkgaXM6CiQkClAoQXxCKSA9IFxmcmFje1AoQSBcY2FwIEIpfXtQKEIpfQokJApGaXJzdCwgSSBkZWZpbmVkIEEsIEIsIGFuZCAkQSBcY2FwIEIkIGZvciBlYWNoIGNhc2UgYXNrZWQuIEkgdXNlZCB0aGUgd2hpY2ggZnVuY3Rpb24gdG8gZmluZSB3aGljaCBlbnRyaWVzIG1ldCB0aGUgY3JpdGVyaWEgaW4gZWFjaCBjYXNlLiBUaGVuIEkgdXNlZCB0aGUgbGVuZ3RoIGZ1bmN0aW9uIHRvIGNvdW50IHRob3NlLiBJIHRoZW4gZGl2aWRlZCBieSB0aGUgdG90YWwgbGVuZ3RoIG9mIHRoZSB2ZWN0b3IgdG8gZ2V0IGEgcGVyY2VudGFnZSAoZXhwcmVzc2VkIGFzIGEgZGVjaW1hbCBiZXR3ZWVuIDAgYW5kIDEpLiAKYGBge3J9CiMgUChYPngpCnAxIDwtIGxlbmd0aCh3aGljaCh4PmxpbC54KSkvbGVuZ3RoKHgpCgojIFAoWT55KQpwMiA8LSBsZW5ndGgod2hpY2goeT5saWwueSkpL2xlbmd0aCh5KQoKIyBQKFg8eCkKcDMgPC0gbGVuZ3RoKHdoaWNoKHg8bGlsLngpKS9sZW5ndGgoeCkKCiMgUChYPnggJiBZPnkpCnA0IDwtIGxlbmd0aCh3aGljaCh4PmxpbC54ICYgeSA+IGxpbC55KSkvbGVuZ3RoKHgpCgojIFAoWDx4ICYgWT55KQpwNSA8LSBsZW5ndGgod2hpY2goeDxsaWwueCAmIHkgPmxpbC55KSkvbGVuZ3RoKHgpCgojIFAoWDx4ICYgWTx5KQpwNiA8LSBsZW5ndGgod2hpY2goeDxsaWwueCAmIHkgPGxpbC55KSkvbGVuZ3RoKHgpCiMgUChYPnggJiBZIDx5KQpwNyA8LSBsZW5ndGgod2hpY2goeD5saWwueCAmIHkgPGxpbC55KSkvbGVuZ3RoKHgpCmBgYAoKCgojIyMgUChYPnh8IFk+eSkKCiQkClAoWD54IHwgWT55KSA9IFxmcmFje1AoWD54IFxjYXAgWT55KX17UChZPnkpfQokJApgYGB7cn0KcGEuYW5kLnBiIDwtIHA0CnBiIDwtIHAyCnJlc3VsdCA8LSBwYS5hbmQucGIvcGIKcmVzdWx0CmBgYApUaGVyZSBpcyBhIDI2JSBjaGFuY2Ugb2YgWD54IHdoZW4gWT55LiAgCgojIyMgUChYPngsIFk+eSkKJCQKUChYPngsIFk+eSkgPSBQKFg+eCBcY2FwIFk+eSkKJCQKCmBgYHtyfQpwNApgYGAKVGhlcmUgaXMgYSAyMCUgY2hhbmNlIG9mICBYPnggYW5kIFk+eS4KCiMjIyBQKFggPCB4IHwgWSA+IHkpCgokJApQKFg+eCB8IFk+eSkgPSBcZnJhY3tQKFg8eCBcY2FwIFk+eSl9e1AoWT55KX0KJCQKYGBge3J9CnBhLmFuZC5wYiA8LSBwNQpwYiA8LSBwMgpyZXN1bHQgPC0gcGEuYW5kLnBiL3BiCnJlc3VsdApgYGAKVGhlcmUgaXMgYSBhIDczJSBjaGFuY2Ugb2YgJFggPCB4JCBnaXZlbiAkWSA+IHkkLiAKCiMjIFRhYmxlIG9mIENvdW50cwoKYGBge3J9CnRhYmxlIDwtIG1hdHJpeChjKHA2LCBwNyxwNSwgcDQpLCBuY29sID0gMikgKmxlbmd0aCh4KQp0YWJsZTIgPC0gdChtYXRyaXgoYyg0LDExLCAxNSwgMSwgNCwgNSwgNSwgMTUsIDIwKSwgbmNvbD0gMykpCnRhYmxlMgpgYGAKIyMgSW5kZXBlbmRlbmNlCklmIHNwbGl0dGluZyB0aGUgdHJhaW5pbmcgZGF0YSBpbiB0aGlzIHdheSBtYWRlIHRoZW0gaW5kZXBlbmRlbnQsIGl0IHdvdWxkIGJlIHRydWUgdGhhdAokJApQKEFCKSA9IFAoQSlQKEIpCiQkCndoZXJlCiQkCkEgPSBbWD4geF0gXFwKQiA9IFtZPnldIFxcCkFCID0gW1g+eCBcY2FwIFk+eV0KJCQKYGBge3J9CkEgPC0gcDEKQiA8LSBwMgpBQiA8LSBwNApBKkIgPT0gQUIKYGBgClRoZXNlIHR3byBkYXRhIHBvaW50cyBhcmUgbm90IGluZGVwZW5kZW50LiBCZWxvdyB3ZSBjYW4gdmVyaWZ5IHRoYXQgd2l0aCBhIGNoaSBzcSB0ZXN0IG9uIHRoZSB0YWJsZSBhYm92ZSAobWludXMgdGhlIHRvdGFsIHJvd3MvY29sdW1ucykKCmBgYHtyfQpjMSA8LSByYmluZCg0LDEpCmMyIDwtIHJiaW5kKDExLDQpCm10eCA8LSBjYmluZChjMSxjMikKbXR4CmNoaXNxLnRlc3QobXR4KQpgYGAKVGhpcyB0aHJvd3MgYSB3YXJuaW5nIGJlY2F1YXNlIHNvbWUgb2YgdGhlIGNlbGxzIGFyZSA8NS4gSW5zdGVhZCwgd2UgY2FuIGRvIGEgRmlzaGVyJ3MgRXhhY3QgVGVzdC4KYGBge3J9CmZpc2hlci50ZXN0KGMocDYscDcpLCBjKHA1LHA0KSkKYGBgCgojIFByb2JsZW0gMgoKIyMgRGVzY3JpcHRpdmUgYW5kIEluZmVyZW50aWFsIFN0YXRpc3RpY3MKYGBge3J9CmRmIDwtIGFzLmRhdGEuZnJhbWUocmVhZC5jc3YoImRhdGEvdHJhaW4uY3N2IikpCgpzdW1tYXJ5KGRmJFNhbGVQcmljZSkKaGlzdChkZiRTYWxlUHJpY2UpCgpzdW1tYXJ5KGRmJEdyTGl2QXJlYSkKaGlzdChkZiRHckxpdkFyZWEpCgpzdW1tYXJ5KGRmJFllYXJCdWlsdCkKaGlzdChkZiRZZWFyQnVpbHQpCgpzdW1tYXJ5KGRmJExvdEFyZWEpCmhpc3QoZGYkTG90QXJlYSwgYnJlYWtzID0gMTAwKQoKcGxvdChkZiRTYWxlUHJpY2UgfiBkZiRHckxpdkFyZWEpCnBsb3QoZGYkU2FsZVByaWNlIH4gZGYkWWVhckJ1aWx0KQpwbG90KGRmJFNhbGVQcmljZSB+IGRmJExvdEFyZWEpCgpgYGAKCmBgYHtyfQpkZjIgPC0gY2JpbmQoZGYkR3JMaXZBcmVhLCBkZiRZZWFyQnVpbHQsIGRmJExvdEFyZWEpCmNvcnJlbGF0aW9uLm1hdHJpeCA8LSBjb3IoZGYyKQpjb3JyZWxhdGlvbi5tYXRyaXgKY29sLjEgPC0gZGYkR3JMaXZBcmVhCmNvbC4yIDwtIGRmJFllYXJCdWlsdApjb2wuMyA8LSBkZiRMb3RBcmVhCgp0ZXN0MSA8LSBjb3IudGVzdChjb2wuMSwgY29sLjIsIGNvbmYubGV2ZWwgPSAuOCkKY29uZi5pbnYuMSA8LSB0ZXN0MSRjb25mLmludAoKdGVzdDIgPC0gY29yLnRlc3QoY29sLjEsIGNvbC4zLCBjb25mLmxldmVsID0gLjgpCmNvbmYuaW52LjIgPC0gdGVzdDIkY29uZi5pbnQKCnRlc3QzIDwtIGNvci50ZXN0KGNvbC4yLCBjb2wuMywgY29uZi5sZXZlbCA9IC44KQpjb25mLmludi4zIDwtIHRlc3QzJGNvbmYuaW50CmBgYAoKSHlwb3RoZXNpcyB0ZXN0OgoKJEhfMCQ6IFRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGVhY2ggdmFyaWFibGUgcGFpciBpcyAwLgokSF9BJDogVGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gaXMgdmFyaWFibGUgcGFpciBpcyBub3QgMC4KYGBge3J9CmNvbmYuaW52LjEKY29uZi5pbnYuMgpjb25mLmludi4zCmBgYAoKIyMjIEFuYWx5c2lzCldoZW4gJFxhbHBoYSA9IC4yJCwgd2UgZmluZCB0aGF0IGxpdmluZyBhcmVhIGFuZCB5ZWFyIGJ1aWx0IGFyZSBzZWVtaW5nbHkgY29ycmVsYXRlZC4gQWRkaXRpb25hbGx5LCB3ZSBmaW5kIHRoYXQgdGhlIGxpdmluZyBhcmVhIGFuZCBsb3QgYXJlYSBhcmUgY29ycmVsYXRlZC4gSG93ZXZlciwgYSBjb3JyZWxhdGlvbiBvZiAwIGlzIHdpdGhpbiBvdXIgY29uZmlkZW5jZSBpbnRlcnZhbCB3aGVuIGNvbXBhcmluZyBsb3Qgc2l6ZSB0byB5ZWFyIGJ1aWx0LiBBZGRpdGlvbmFsbHksIHRoZSBmYW1pbHktd2lzZSBlcnJvciByYXRlIGlzIApgYGB7cn0KRldFIDwtIDEtKDEtLjIpXjMKRldFCmBgYAp3aGljaCBtZWFucyB0aGVyZSBpcyBhIDQ5JSBjaGFuY2Ugb2YgYSBUeXBlIDEgRXJyb3IuIFRoaXMgaXMgdmVyeSB3b3JyeWluZyBjb25zaWRlcmluZyB3ZSBvbmx5IGRpZCAzIHRlc3RzLgoKIyMgTGluZWFyIEFsZ2VicmEgYW5kIENvcnJlbGF0aW9uCkludmVydCB5b3VyIDMgeCAzIGNvcnJlbGF0aW9uIG1hdHJpeCBmcm9tIGFib3ZlLiAoVGhpcyBpcyBrbm93biBhcyB0aGUgcHJlY2lzaW9uIG1hdHJpeCBhbmQgY29udGFpbnMgdmFyaWFuY2UgaW5mbGF0aW9uIGZhY3RvcnMgb24gdGhlIGRpYWdvbmFsLikgTXVsdGlwbHkgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeCBieSB0aGUgcHJlY2lzaW9uIG1hdHJpeCwgYW5kIHRoZW4gbXVsdGlwbHkgdGhlIHByZWNpc2lvbiBtYXRyaXggYnkgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeC4gQ29uZHVjdCBMVSBkZWNvbXBvc2l0aW9uIG9uIHRoZSBtYXRyaXguICAKClRoZSBpbnZlcnNlIGV4aXN0cyBpZiBhbmQgb25seSBpZiB0aGUgZGV0ZXJtaW5hbnQgaXMgbm90IDAuIEluIHRoaXMgY2FzZXMsIHRoZSBkZXRlcm1pbmFudCA9IC44OS4gU28sIGFuIGludmVyc2UgZXhpc3RzLiAKCmBgYHtyfQpkZXQoY29ycmVsYXRpb24ubWF0cml4KQpgYGAKQW4gaW52ZXJzZSBvZiBhIG1hdHJpeCBpcyBkZWZpbmVkIGJ5IAokJApBIEFeey0xfSA9IEkgPSBBXnstMX0gQSAKJCQKVGhlIG1lY2hhbmljcyBvZiB0aGlzIGNhbGN1bGF0aW9uIGludm9sdmUgZmluZGluZyBhIGRldGVybWluYW50LCBhbiBhZGp1Z2F0ZSwgYW5kIG11bHRpcGx5aW5nIHRoZW0gdG9nZXRoZXIuIEhvd2V2ZXIsIFIgZ2l2ZXMgdXMgc29tZSBoYW5keSB0b29scyBmb3IgZG9pbmcgbWF0cml4IG1hdGguIEJlbG93LCBJIHVzZSB0aGUgbWF0bGliIGxpYnJhcnkgdG8gZmluZCBhbiBpbnZlcnNlLgpgYGB7cn0KbGlicmFyeShtYXRsaWIpCnByZWNpc2lvbi5tYXRyaXggPC0gc29sdmUoY29ycmVsYXRpb24ubWF0cml4KQptdHgxIDwtIGNvcnJlbGF0aW9uLm1hdHJpeCAlKiUgcHJlY2lzaW9uLm1hdHJpeAptdHgyIDwtIHByZWNpc2lvbi5tYXRyaXggJSolIGNvcnJlbGF0aW9uLm1hdHJpeAptdHgxCm10eDIKYGBgCkxVIGRlY29tcG9zaXRpb24gYWxsb3dzIGZvciBhIHF1aWNrIHJlY2FsY3VsYXRpb24gaWYgdGhlIHJpZ2h0IGhhbmQgc2lkZSBvZiBvdXIgZXF1YXRpb24gY2hhbmdlcyAoYnksIHNheSBhZGRpbmcgbW9yZSBkYXRhKS4gVGhpcyBhbGxvd3MgdXMgdG8gcXVpY2tseSBjb21wdXRlIHRoZSBuZXcgdmFsdWVzIGZvciB0aGUgJFxvdmVybGluZXtifSQuIEJlbG93IEkgdXNlIHRoZSBsdV9mYWN0b3JpemF0aW9uIGZ1bmN0aW9uIEkgd3JvdGUgYWxsIHRoZSB3YXkgYmFjayBpbiBhc3NpZ25tZW50IDEuIApgYGB7cn0KbHVfZmFjdG9yaXphdGlvbiA8LSBmdW5jdGlvbihBKXsKICBsaWJyYXJ5KG1hdHJpeGNhbGMpCiAgaWYgKG5yb3coQSkhPSBuY29sKEEpKXsKICAgIHN0b3AoIk11c3QgYmUgYSBzcXVhcmUgbWF0cml4ISIpCiAgfQogIHNpemUgPC0gbnJvdyhBKQogIEwgPC0gbWF0cml4ICgwLCBucm93ID0gc2l6ZSwgbmNvbCA9IHNpemUpCiAgVSA8LSBtYXRyaXggKDAsIG5yb3cgPSBzaXplLCBuY29sID0gc2l6ZSkKICBmb3IgKGkgaW4gMTpzaXplKXsKICAgIExbaSxpXSA9IDEKICB9CiAgZm9yICggaSBpbiAxOnNpemUgKSB7CiAgICAgICAgdXAgPC0gaSArIDEKICAgICAgICBkb3duIDwtIGkgLSAxCiAgICAgICAgZm9yICggaiBpbiAxOnNpemUgKSB7CiAgICAgICAgICAgIFVbaSxqXSA8LSBBW2ksal0KICAgICAgICAgICAgaWYgKCBkb3duID4gMCApIHsKICAgICAgICAgICAgICAgIGZvciAoIGsgaW4gMTpkb3duICkgewogICAgICAgICAgICAgICAgICAgIFVbaSxqXSA8LSBVW2ksal0gLSBMW2ksa10gKiBVW2ssal0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBpZiAoIHVwIDw9IHNpemUgKSB7CiAgICAgICAgICAgIGZvciAoIGogaW4gdXA6c2l6ZSApIHsKICAgICAgICAgICAgICAgIExbaixpXSA8LSBBW2osaV0KICAgICAgICAgICAgICAgIGlmICggZG93biA+IDAgKSB7CiAgICAgICAgICAgICAgICAgICAgZm9yICggayBpbiAxOmRvd24gKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIExbaixpXSA8LSBMW2osaV0gLSBMW2osa10gKiBVW2ssaV0KICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBMW2osaV0gPC0gTFtqLGldIC8gVVtpLGldCiAgICAgICAgICAgIH0gICAgCiAgICAgICAgfQogIH0KICBhbnN3ZXIgPC0gbGlzdChMLFUpCiAgcmV0dXJuKGFuc3dlcikKfQoKTCA8LSBsdV9mYWN0b3JpemF0aW9uKGNvcnJlbGF0aW9uLm1hdHJpeClbMV0KVSA8LSBsdV9mYWN0b3JpemF0aW9uKGNvcnJlbGF0aW9uLm1hdHJpeClbMl0KTCA8LSBhcy5kYXRhLmZyYW1lKEwpClUgPC0gYXMuZGF0YS5mcmFtZShVKQpMIDwtIGFzLm1hdHJpeChMKQpVIDwtIGFzLm1hdHJpeChVKQpMClUKYGBgCmBgYHtyfQpMICUqJSBVCmBgYApCZWNhdXNlIExVIGRlY29tcG9zaXRpb24gaXMgbm90IHVuaXF1ZSwgSSBjYW5ub3QgY2hlY2sgaXQgZGlyZWN0bHkuIEhvd2V2ZXIsIEkgY2FuIG1ha2Ugc3VyZSB0aGF0IEwgKiBVIHJlc3VsdHMgaW4gdGhlIHNhbWUgYW5zd2VyIHdoZXRoZXIgSSB1c2UgbXkgZnVuY3Rpb24gb3IgdGhlIG9uZSBmcm9tIHByYWNtYS4gCmBgYHtyfQpsaWJyYXJ5KHByYWNtYSkKcmVzIDwtIGx1KGNvcnJlbGF0aW9uLm1hdHJpeCkKcmVzJEwgJSolIHJlcyRVCmBgYAoKIyMgQ2FsY3VsdXMtQmFzZWQgUHJvYmFiaWxpdHkgYW5kIFN0YXRpc3RpY3MKRmluZCB0aGUgb3B0aW1hbCB2YWx1ZSBvZiAkXGxhbWJkYSQgZm9yIHRoaXMgZGlzdHJpYnV0aW9uLCBhbmQgdGhlbiB0YWtlIDEwMDAgc2FtcGxlcyBmcm9tIHRoaXMgZXhwb25lbnRpYWwgZGlzdHJpYnV0aW9uIHVzaW5nIHRoaXMgdmFsdWUgKGUuZy4sIHJleHAoMTAwMCwgJFxsYW1iZGEkKS4gIFBsb3QgYSBoaXN0b2dyYW0gYW5kIGNvbXBhcmUgaXQgd2l0aCBhIGhpc3RvZ3JhbSBvZiB5b3VyIG9yaWdpbmFsIHZhcmlhYmxlLiAgIFVzaW5nIHRoZSBleHBvbmVudGlhbCBwZGYsIGZpbmQgdGhlIDV0aCBhbmQgOTV0aCBwZXJjZW50aWxlcyB1c2luZyB0aGUgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gKENERikuICAgQWxzbyBnZW5lcmF0ZSBhIDk1JSBjb25maWRlbmNlIGludGVydmFsIGZyb20gdGhlIGVtcGlyaWNhbCBkYXRhLCBhc3N1bWluZyBub3JtYWxpdHkuICBGaW5hbGx5LCBwcm92aWRlIHRoZSBlbXBpcmljYWwgNXRoIHBlcmNlbnRpbGUgYW5kIDk1dGggcGVyY2VudGlsZSBvZiB0aGUgZGF0YS4gIERpc2N1c3MuCmBgYHtyfQpsaWJyYXJ5KE1BU1MpCmF0dGFjaChkYXRhKQpwYXIobWZyb3cgPSBjKDEsMikpCmhpc3QoZGYkR3JMaXZBcmVhKQoKbW9kZWwyIDwtIGZpdGRpc3RyKHggPSBkZiRHckxpdkFyZWEsICJleHBvbmVudGlhbCIpCmV4IDwtIHJleHAoMTAwMCwgcmF0ZSA9IG1vZGVsMiRlc3RpbWF0ZSkKaGlzdChleCkKbG93ZXIgPC0gcWV4cChwID0gLjA1LCByYXRlID0gbW9kZWwyJGVzdGltYXRlKQp1cHBlciA8LSBxZXhwKHAgPSAuOTUsIHJhdGUgPSBtb2RlbDIkZXN0aW1hdGUpCnJhbmdlMSA8LSBjKGxvd2VyLHVwcGVyKQpyYW5nZTEKYGBgClRoZSBhYm92ZSBzaG93IHRoZSA1IGFuZCA5NSBwZXJjZW50aWxlcyBmb3IgdGhlIGZpdHRlZCBleHBvbmVudGlhbCBmdW5jdGlvbi4KYGBge3J9CnVwcGVyIDwtIG1lYW4oZGYkR3JMaXZBcmVhKSArIHNkKGRmJEdyTGl2QXJlYSkqMS42NQpsb3dlciA8LSBtZWFuKGRmJEdyTGl2QXJlYSkgLSBzZChkZiRHckxpdkFyZWEpKjEuNjUKcmFuZ2UyIDwtIGMobG93ZXIsIHVwcGVyKQpyYW5nZTIKYGBgClRoZSBhYm92ZSBzaG93cyB0aGUgNSBhbmQgOTUgcGVyY2VudGlsZXMgZm9yIGEgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgZnVuY3Rpb24gd2l0aCB0aGUgc2FtZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gYXMgb3VyIG9yaWdpbmFsIGRhdGEuCmBgYHtyfQpsb3dlciA8LSBxdWFudGlsZShkZiRHckxpdkFyZWEsIC4wNSkKdXBwZXIgPC0gcXVhbnRpbGUoZGYkR3JMaXZBcmVhLCAuOTUpCnJhbmdlMyA8LSBjKGxvd2VyLCB1cHBlcikKcmFuZ2UzCmBgYApCeSBsb29raW5nIGF0IHRoZSByZXNpZHVhbHMgb2YgdGhlIHF1YW50aWxlcyAoYXMgd2VsbCBhcyB0aGUgaW5pdGlhbCBoaXN0b2dyYW1zKSwgd2Ugc2VlIHRoYXQgb3VyIGRhdGEgaXMgbXVjaCBtb3JlIHN1aXRlZCB0byBhIG5vcm1hbCBkaXN0cmlidXR1dGlvbiB0aGFuIGFuIGV4cG9uZW50aWFsIG9uZS4KCiMjIE11bHRpcGxlIExpbmVhciBSZWdyZXNzaW9uCgpCZWxvdywgSSBtYWRlIGEgbW9kZWwgYXR0ZW1wdGVkIHRvIG1lYXN1cmUgc2FsZSBwcmljZSBmcm9tIE92ZXJhbGwgUXVhbGl0eSwgR3JhZGVkIExpdmluZyBBcmVhLCBTYWxlIFllYXIgYW5kIE92ZXJhbGwgQ29uZGl0aW9uLiBCZWxvdyBpcyBhcmUgdGhlIHN1bW1hcnkgcGxvdHMgZm9yIHRoZSBtb2RlbCBhbmQgaXRzIHN1bW1hcnkgc3RhdGlzdGljcy4KYGBge3J9CgpkYXRhIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQoZGYkT3ZlcmFsbFF1YWwsIGRmJEdyTGl2QXJlYSwgZGYkWWVhckJ1aWx0LCBkZiRPdmVyYWxsQ29uZCwgZGYkU2FsZVByaWNlLCBkZiRCZWRyb29tQWJ2R3IsIGRmJEdhcmFnZVR5cGUpKQpjb2xuYW1lcyhkYXRhKSA8LSBjKCJRdWFsaXR5IiwgIlNpemUiLCAiU2FsZVllYXIiLCAgIkNvbmRpdGlvbiIsICJQcmljZSIsICJSb29tcyIsICJHYXJhZ2VTaXplIikKCm1vZGVsMyA8LSBsbShQcmljZSB+SShRdWFsaXR5Kio1KSArIEkoU2l6ZSoqMikgKyBTYWxlWWVhciArIENvbmRpdGlvbiArIFJvb21zLCBkYXRhID0gZGF0YSkKCnBsb3QobW9kZWwzKQpzdW1tYXJ5KG1vZGVsMykKYGBgCgpUaGlzIG1vZGVsIGRvZXMgYSBkZWNlbnQgam9iIG1vZGVsbGluZyBob3VzZSBwcmljZXMsIGV4Y2VwdCBmb3IgdGhlIG1vc3QgbHV4dXJpb3VzIG9mIGhvdXNlcy4gVGhlIHJlc2lkdWFscyBhcHBlYXIgdG8gYmUgcmVsYXRpdmVseSBub3JtYWxseSBkaXN0cmlidXRlZCBhYm91dCB0aGUgZml0dGVkIHZhbHVlcywgZXhjZXB0IGZvciB0aGUgdGFpbHMgd2hlcmUgd2Ugc2ltcGx5IGRvbid0IGhhdmUgbWFueSBkYXRhIHBvaW50cy4gQWRkaXRpb25hbGx5LCB0aGUgTm9ybWFsIFEtUSBwbG90IHNob3dzIHVzIHRoYXQgb3VyIG1vZGVsIGZpdHMgb3VyIGRhdGEgdmVyeSBuZWF0bHkgdW50aWwgdGhlIG1vc3QgbHV4dXJpb3VzIG9mIGhvdXNlcy4gVGhpcyB3b3VsZCBzdWdnZXN0IHRoYXQgdGhlc2UgbHV4dXJ5IGhvbWVzIGhhdmUgcHJpY2VzIGJleW9uZCBzaXplLCBxdWFsaXR5LCBzYWxlIHllYXIsIGFuZCBjb25kaXRpb24uIFRoZSBvdGhlciBvcHRpb24gaXMgdGhhdCB0aGVzZSBtZWFzdXJlcyBhcmUgbm90IGdyYW51bGFyIGVub3VnaCBpbiBvdXIgZGF0YSB0byBkaWZmZXJlbnRpYXRlIHRoZSBtb3N0IGV4cXVpc2l0aWUgaG91c2VzLiBXaXRoIGFuIFJeMiB2YWx1ZSBvZiAuNzUgYW5kIGEgcmVsYXRpdmVseSBzbWFsbCBudW1iZXIgb2YgZmVhdHVyZXMsIEkgYW0gc2F0aXNmaWVkIHdpdGggdGhpcyBtb2RlbC4gCgpCZWxvdywgSSB1c2UgdGhlIHRlc3QgZGF0YSBzZXQgdG8gdGVzdCBteSBtb2RlbC4gSSB0aGVuIHdyaXRlIGl0IHRvIGEgY3N2IGluIHRoZSBkYXRhIGZvbGRlciBjYWxsZWQgImthZ2dsZV9yZXN1bHRzLmNzdiIuIAoKYGBge3J9CmRmMjwtIHJlYWQuY3N2KCJkYXRhL3Rlc3QuY3N2IikKbmV3LmRhdGEgPC0gYXMuZGF0YS5mcmFtZShjYmluZChkZjIkT3ZlcmFsbFF1YWwsIGRmMiRHckxpdkFyZWEsIGRmMiRZZWFyQnVpbHQsIGRmMiRPdmVyYWxsQ29uZCwgZGYyJEJlZHJvb21BYnZHcikpCmNvbG5hbWVzKG5ldy5kYXRhKSA8LSBjKCJRdWFsaXR5IiwgIlNpemUiLCAiU2FsZVllYXIiLCAgIkNvbmRpdGlvbiIsICJSb29tcyIpCgpwcmVkaWN0aW9ucyA8LSBhcy5kYXRhLmZyYW1lKHJvdW5kKHByZWRpY3QubG0ob2JqZWN0ID0gbW9kZWwzLCBuZXdkYXRhID0gbmV3LmRhdGEpLCAyKSkKaW5kZXggPC0gZGYyJElkCnByZWRpY3Rpb25zIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQoaW5kZXgsIHByZWRpY3Rpb25zKSkKY29sbmFtZXMocHJlZGljdGlvbnMpIDwtIGMoIklEIiwgIlNhbGVQcmljZSIpCndyaXRlLmNzdihwcmVkaWN0aW9ucywgZmlsZSA9ICdkYXRhL2thZ2dsZV9yZXN1bHRzLmNzdicsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCkFmdGVyIHN1Ym1pdHRpbmcgdGhlIGNzdiBtYW51YWxseSB0byBLYWdnbGUsIEkgcmFua2VkIDQ2MTIgdW5kZXIgdGhlIGhhbmRsZSBAc2ltcGx5bWF0aGVtYXRpY3MuIAoKCiMgVmlkZW8gCgpodHRwczovL3d3dy51c2Vsb29tLmNvbS9zaGFyZS9hNWNkZWFmNDdlM2E0OGU3YmNjYmQxNzAwMGJkM2VkOAoKCgoKCgo=