Please download and load data “Assignment_2.Rdata” from Canvas.

This notebook includes suggested solutions to the analytical questions in Assignment 2. For some questions, your answers might not be exactly the same as the suggested solutions. These should be fine in terms of grading. For example, for Question 3a, depending on how the train and test set are splitted, you may have different optimal bandwidths.

load("Assignment_2.Rdata")
ls()
## [1] "ad.trial"     "airbnb"       "app.installs"

Question 1

This question uses “app.installs”:

Question 1a

We first construct a function which estimates the Bass model with a vector of adoptions (i.e., installations) at different time periods (i.e., days) as input.

# create a function to estimate the Bass model 
estimate_bass <- function(x) {
  
  # create a new variable of cumulative sales 
  cum.x <- cumsum(as.numeric(x)) # to avoid integer overflow
  
  # run a regression with sales as DV and cum_sales and cum_sales^2 as IVs
  mdl <- lm(x ~ cum.x + I(cum.x^2))
  
  # get the coefficients 
  # a: the intercept 
  # b: the coefficient of cumulative sales
  # c: the coefficient of squared cumulative sales 
  a <- mdl$coefficients[1]
  b <- mdl$coefficients[2]
  c <- mdl$coefficients[3]
  
  # solving for p, q and M with a, b and c
  M1 <- (-b-sqrt(b^2-4*a*c))/(2*c)
  M2 <- (-b+sqrt(b^2-4*a*c))/(2*c)
  M <- max(M1,M2)
  
  p <- a/M
  q <- -c*M
  
  # output a named vector 
  bass.par <- c(p,q,M)
  names(bass.par) <- c("p","q","M")
  
  return(bass.par)
}

We then apply the function to the whole list to get the Bass parameters of all apps.

apps_bass_paras <- lapply(app.installs,estimate_bass)

# transform into a data frame for easy reading
temp <- t(matrix(unlist(apps_bass_paras),
                 nrow = 3,
                 ncol = 291
                 )
          )
apps_bass_paras <- data.frame(apps = names(apps_bass_paras),
                              p = temp[,1],
                              q = temp[,2],
                              M = temp[,3],
                              stringsAsFactors=F)
apps_bass_paras[order(apps_bass_paras$apps),]

Question 2

This question uses “ad.trial” as the data.

Question 2a

For this question, we only need to run three regressions with the treatment variable using different outcomes. As noted in the question, we need to transform the variables in the estimations.

The ATE on page views:

page_views <- lm(log(pageviews)~treatment,data = ad.trial)
summary(page_views)
## 
## Call:
## lm(formula = log(pageviews) ~ treatment, data = ad.trial)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -0.9970 -0.2867 -0.1447  0.3769  0.7068 
## 
## Coefficients:
##                     Estimate Std. Error t value Pr(>|t|)    
## (Intercept)         5.973689   0.003473 1720.02   <2e-16 ***
## treatmentcurrent.ad 0.191438   0.004912   38.98   <2e-16 ***
## treatmentother.ad   0.141705   0.004912   28.85   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.3473 on 29997 degrees of freedom
## Multiple R-squared:  0.05172,    Adjusted R-squared:  0.05166 
## F-statistic:   818 on 2 and 29997 DF,  p-value: < 2.2e-16

The ATE on reservations:

reservations <- lm(log(reservations)~treatment,data = ad.trial)
summary(reservations)
## 
## Call:
## lm(formula = log(reservations) ~ treatment, data = ad.trial)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.82164 -0.13912 -0.01262  0.13117  0.65744 
## 
## Coefficients:
##                     Estimate Std. Error  t value Pr(>|t|)    
## (Intercept)         3.506420   0.001902 1843.826   <2e-16 ***
## treatmentcurrent.ad 0.002710   0.002689    1.008    0.314    
## treatmentother.ad   0.205587   0.002689   76.443   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1902 on 29997 degrees of freedom
## Multiple R-squared:  0.204,  Adjusted R-squared:  0.204 
## F-statistic:  3845 on 2 and 29997 DF,  p-value: < 2.2e-16

The ATE on conversion rates (CR):

# create a new variable of conversion rate
ad.trial$CR <- ad.trial$reservations/ad.trial$pageviews
CR <- lm(log(CR/(1-CR))~treatment,data = ad.trial)
summary(CR)
## 
## Call:
## lm(formula = log(CR/(1 - CR)) ~ treatment, data = ad.trial)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.28426 -0.20741  0.03596  0.20195  0.83512 
## 
## Coefficients:
##                      Estimate Std. Error t value Pr(>|t|)    
## (Intercept)         -2.375566   0.002627 -904.13   <2e-16 ***
## treatmentcurrent.ad -0.205930   0.003716  -55.42   <2e-16 ***
## treatmentother.ad    0.070763   0.003716   19.04   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.2627 on 29997 degrees of freedom
## Multiple R-squared:  0.1664, Adjusted R-squared:  0.1663 
## F-statistic:  2993 on 2 and 29997 DF,  p-value: < 2.2e-16

Question 2c.1

For this quesiton, we run regressions which interact the types of restaurants (ad.trial:restaurant_type) and the treatment variable (ad.trial:treatment).

The heterogeneous treatment effects on page views:

hetero_page_views <- lm(log(pageviews)~treatment*restaurant_type,data = ad.trial)
summary(hetero_page_views)
## 
## Call:
## lm(formula = log(pageviews) ~ treatment * restaurant_type, data = ad.trial)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.71762 -0.06745  0.00480  0.07655  0.41490 
## 
## Coefficients:
##                                           Estimate Std. Error  t value Pr(>|t|)
## (Intercept)                               5.694350   0.001449 3930.675   <2e-16
## treatmentcurrent.ad                       0.225303   0.002049  109.970   <2e-16
## treatmentother.ad                         0.142213   0.002049   69.414   <2e-16
## restaurant_typechain                      0.698349   0.002291  304.877   <2e-16
## treatmentcurrent.ad:restaurant_typechain -0.084662   0.003239  -26.135   <2e-16
## treatmentother.ad:restaurant_typechain   -0.001271   0.003239   -0.392    0.695
##                                             
## (Intercept)                              ***
## treatmentcurrent.ad                      ***
## treatmentother.ad                        ***
## restaurant_typechain                     ***
## treatmentcurrent.ad:restaurant_typechain ***
## treatmentother.ad:restaurant_typechain      
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1122 on 29994 degrees of freedom
## Multiple R-squared:  0.901,  Adjusted R-squared:  0.901 
## F-statistic: 5.46e+04 on 5 and 29994 DF,  p-value: < 2.2e-16

The heterogeneous treatment effects on reservations:

hetero_reservations <- lm(log(reservations)~treatment*restaurant_type,data = ad.trial)
summary(hetero_reservations)
## 
## Call:
## lm(formula = log(reservations) ~ treatment * restaurant_type, 
##     data = ad.trial)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.96377 -0.09089  0.00970  0.09632  0.51531 
## 
## Coefficients:
##                                           Estimate Std. Error  t value Pr(>|t|)
## (Intercept)                               3.391496   0.001748 1940.607   <2e-16
## treatmentcurrent.ad                       0.001477   0.002472    0.598     0.55
## treatmentother.ad                         0.225755   0.002472   91.342   <2e-16
## restaurant_typechain                      0.287311   0.002763  103.975   <2e-16
## treatmentcurrent.ad:restaurant_typechain  0.003082   0.003908    0.789     0.43
## treatmentother.ad:restaurant_typechain   -0.050420   0.003908  -12.902   <2e-16
##                                             
## (Intercept)                              ***
## treatmentcurrent.ad                         
## treatmentother.ad                        ***
## restaurant_typechain                     ***
## treatmentcurrent.ad:restaurant_typechain    
## treatmentother.ad:restaurant_typechain   ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1354 on 29994 degrees of freedom
## Multiple R-squared:  0.5967, Adjusted R-squared:  0.5966 
## F-statistic:  8876 on 5 and 29994 DF,  p-value: < 2.2e-16

Question 3

This question uses “airbnb” as the data.

Quesiton 3a

Here we try to select the “optimal” bandwidth with the 2-fold cross-validation method. The optimal bandwidth is the one that has the smallest mean squared error (MSE) on the testing sample. We test 5 bandwidth. To select the optimal bandwidth, we adopt a regression-based estimator of neighborhood effects. We estimate a regression equation like this: \(Price = Neighbourhood + Distance + Neighbourhood\times Distance + e\).

#===== to split data into test and train set --------------------

# Set seeds for replication 
set.seed(12345) 

# Permutate the index
N <- dim(airbnb)[1]
idx <- sample(1:N,N,replace = F)

# To obtain a random subsample as train and test data
train <- airbnb[idx[1:(N/2)],]
test <- airbnb[idx[(N/2+1):N],]

#===== To select optimal bandwidths ------------------------------
# Here we test 5 bandwidths. 

# standard deviation of distance
H <- rep(sd(airbnb$distance),5)*seq(0.1,0.9,0.2)

# a vector to store MSE 
H.fit <- rep(0,5)

# Loop over 5 bandwidths
for (i in 1:5) {
  
  # This is the model specified above. More complex method may be used here. 
  mdl <- lm(price~neighbourhood*distance,train[abs(train$distance)<H[i],])
  
  # To obtain the predicted outcome for test data. 
  price.hat <- predict(mdl,newdata = test[abs(test$distance)<H[i],])
  
  # To calcualte and store MSE
  H.fit[i] <- mean((test$price[abs(test$distance)<H[i]]-price.hat)^2)
}

# To get the bandwidth of the one with mininum MSE
H <- H[H.fit==min(H.fit)]

# The MSE of the 5 bandwidths:
H.fit
## [1] 0.2796239 0.2795826 0.2843565 0.2734253 0.2711636
#The optimal bandwidth is:
H
## [1] 1.126712

Question 3b.1

After having the optimal bandwidth \(H\), we rerun the model with the full data. This gives us a “local average treatment effects” (LATE), i.e., the neighborhood effects on prices for hotels located close to the “boarder” of the two neighborhoods.

#Rerun a model on full data with bandwidth set to H
mdl <- lm(price~neighbourhood*distance,airbnb[abs(airbnb$distance)<H,])

# The neighborhood effect is measured by the coefficient of the neighborhood dummy.
summary(mdl)
## 
## Call:
## lm(formula = price ~ neighbourhood * distance, data = airbnb[abs(airbnb$distance) < 
##     H, ])
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.07310 -0.33320 -0.04758  0.28273  2.90143 
## 
## Coefficients:
##                                    Estimate Std. Error t value Pr(>|t|)    
## (Intercept)                         5.24018    0.03052 171.706  < 2e-16 ***
## neighbourhoodCentrum-West          -0.08917    0.04003  -2.228 0.025988 *  
## distance                            0.16662    0.04898   3.402 0.000681 ***
## neighbourhoodCentrum-West:distance -0.19706    0.06462  -3.050 0.002315 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.5106 on 2491 degrees of freedom
## Multiple R-squared:  0.005084,   Adjusted R-squared:  0.003885 
## F-statistic: 4.243 on 3 and 2491 DF,  p-value: 0.005337

Question 3b.2

To rerun the model with control variables added to check the robustness of the LATE.

#Rerun a model on full data with bandwidth set to H
mdl.1 <- lm(price~neighbourhood*distance + 
            room_type + 
            reviews_per_month + 
            review_scores_rating + 
            host_is_superhost,airbnb[abs(airbnb$distance)<H,])

# The neighborhood effect is measured by the coefficient of the neighborhood dummy.
summary(mdl.1)
## 
## Call:
## lm(formula = price ~ neighbourhood * distance + room_type + reviews_per_month + 
##     review_scores_rating + host_is_superhost, data = airbnb[abs(airbnb$distance) < 
##     H, ])
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.14868 -0.31188 -0.03687  0.27048  2.81899 
## 
## Coefficients:
##                                     Estimate Std. Error t value Pr(>|t|)    
## (Intercept)                         4.488548   0.148381  30.250  < 2e-16 ***
## neighbourhoodCentrum-West          -0.079688   0.037094  -2.148 0.031787 *  
## distance                            0.193163   0.045476   4.248 2.24e-05 ***
## room_typeEntire home/apt            0.401022   0.024010  16.702  < 2e-16 ***
## room_typeShared room               -0.503600   0.180045  -2.797 0.005196 ** 
## reviews_per_month                  -0.016357   0.007995  -2.046 0.040874 *  
## review_scores_rating                0.004974   0.001531   3.249 0.001174 ** 
## host_is_superhostTRUE               0.066735   0.025176   2.651 0.008081 ** 
## neighbourhoodCentrum-West:distance -0.232013   0.059954  -3.870 0.000112 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4726 on 2485 degrees of freedom
##   (1 observation deleted due to missingness)
## Multiple R-squared:  0.1485, Adjusted R-squared:  0.1457 
## F-statistic: 54.15 on 8 and 2485 DF,  p-value: < 2.2e-16
LS0tDQp0aXRsZTogJ0Fzc2lnbm1lbnQgMjogQW5hbHl0aWNzJw0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KLS0tDQoNCiMjIyAqKlBsZWFzZSBkb3dubG9hZCBhbmQgbG9hZCBkYXRhICJBc3NpZ25tZW50XzIuUmRhdGEiIGZyb20gQ2FudmFzLioqDQo+IFRoaXMgbm90ZWJvb2sgaW5jbHVkZXMgc3VnZ2VzdGVkIHNvbHV0aW9ucyB0byB0aGUgYW5hbHl0aWNhbCBxdWVzdGlvbnMgaW4gQXNzaWdubWVudCAyLiBGb3Igc29tZSBxdWVzdGlvbnMsIHlvdXIgYW5zd2VycyBtaWdodCBub3QgYmUgZXhhY3RseSB0aGUgc2FtZSBhcyB0aGUgc3VnZ2VzdGVkIHNvbHV0aW9ucy4gVGhlc2Ugc2hvdWxkIGJlIGZpbmUgaW4gdGVybXMgb2YgZ3JhZGluZy4gRm9yIGV4YW1wbGUsIGZvciBRdWVzdGlvbiAzYSwgZGVwZW5kaW5nIG9uIGhvdyB0aGUgdHJhaW4gYW5kIHRlc3Qgc2V0IGFyZSBzcGxpdHRlZCwgeW91IG1heSBoYXZlIGRpZmZlcmVudCBvcHRpbWFsIGJhbmR3aWR0aHMuICANCg0KYGBge3J9DQpsb2FkKCJBc3NpZ25tZW50XzIuUmRhdGEiKQ0KbHMoKQ0KYGBgDQoNCiMjICoqUXVlc3Rpb24gMSoqDQo+IFRoaXMgcXVlc3Rpb24gdXNlcyAiYXBwLmluc3RhbGxzIjogDQoNCiMjIyAqKlF1ZXN0aW9uIDFhKioNCj4gV2UgZmlyc3QgY29uc3RydWN0IGEgZnVuY3Rpb24gd2hpY2ggZXN0aW1hdGVzIHRoZSBCYXNzIG1vZGVsIHdpdGggYSB2ZWN0b3Igb2YgYWRvcHRpb25zIChpLmUuLCBpbnN0YWxsYXRpb25zKSBhdCBkaWZmZXJlbnQgdGltZSBwZXJpb2RzIChpLmUuLCBkYXlzKSBhcyBpbnB1dC4gDQoNCmBgYHtyfQ0KIyBjcmVhdGUgYSBmdW5jdGlvbiB0byBlc3RpbWF0ZSB0aGUgQmFzcyBtb2RlbCANCmVzdGltYXRlX2Jhc3MgPC0gZnVuY3Rpb24oeCkgew0KICANCiAgIyBjcmVhdGUgYSBuZXcgdmFyaWFibGUgb2YgY3VtdWxhdGl2ZSBzYWxlcyANCiAgY3VtLnggPC0gY3Vtc3VtKGFzLm51bWVyaWMoeCkpICMgdG8gYXZvaWQgaW50ZWdlciBvdmVyZmxvdw0KICANCiAgIyBydW4gYSByZWdyZXNzaW9uIHdpdGggc2FsZXMgYXMgRFYgYW5kIGN1bV9zYWxlcyBhbmQgY3VtX3NhbGVzXjIgYXMgSVZzDQogIG1kbCA8LSBsbSh4IH4gY3VtLnggKyBJKGN1bS54XjIpKQ0KICANCiAgIyBnZXQgdGhlIGNvZWZmaWNpZW50cyANCiAgIyBhOiB0aGUgaW50ZXJjZXB0IA0KICAjIGI6IHRoZSBjb2VmZmljaWVudCBvZiBjdW11bGF0aXZlIHNhbGVzDQogICMgYzogdGhlIGNvZWZmaWNpZW50IG9mIHNxdWFyZWQgY3VtdWxhdGl2ZSBzYWxlcyANCiAgYSA8LSBtZGwkY29lZmZpY2llbnRzWzFdDQogIGIgPC0gbWRsJGNvZWZmaWNpZW50c1syXQ0KICBjIDwtIG1kbCRjb2VmZmljaWVudHNbM10NCiAgDQogICMgc29sdmluZyBmb3IgcCwgcSBhbmQgTSB3aXRoIGEsIGIgYW5kIGMNCiAgTTEgPC0gKC1iLXNxcnQoYl4yLTQqYSpjKSkvKDIqYykNCiAgTTIgPC0gKC1iK3NxcnQoYl4yLTQqYSpjKSkvKDIqYykNCiAgTSA8LSBtYXgoTTEsTTIpDQogIA0KICBwIDwtIGEvTQ0KICBxIDwtIC1jKk0NCiAgDQogICMgb3V0cHV0IGEgbmFtZWQgdmVjdG9yIA0KICBiYXNzLnBhciA8LSBjKHAscSxNKQ0KICBuYW1lcyhiYXNzLnBhcikgPC0gYygicCIsInEiLCJNIikNCiAgDQogIHJldHVybihiYXNzLnBhcikNCn0NCmBgYA0KPiBXZSB0aGVuIGFwcGx5IHRoZSBmdW5jdGlvbiB0byB0aGUgd2hvbGUgbGlzdCB0byBnZXQgdGhlIEJhc3MgcGFyYW1ldGVycyBvZiBhbGwgYXBwcy4gDQoNCmBgYHtyfQ0KYXBwc19iYXNzX3BhcmFzIDwtIGxhcHBseShhcHAuaW5zdGFsbHMsZXN0aW1hdGVfYmFzcykNCg0KIyB0cmFuc2Zvcm0gaW50byBhIGRhdGEgZnJhbWUgZm9yIGVhc3kgcmVhZGluZw0KdGVtcCA8LSB0KG1hdHJpeCh1bmxpc3QoYXBwc19iYXNzX3BhcmFzKSwNCiAgICAgICAgICAgICAgICAgbnJvdyA9IDMsDQogICAgICAgICAgICAgICAgIG5jb2wgPSAyOTENCiAgICAgICAgICAgICAgICAgKQ0KICAgICAgICAgICkNCmFwcHNfYmFzc19wYXJhcyA8LSBkYXRhLmZyYW1lKGFwcHMgPSBuYW1lcyhhcHBzX2Jhc3NfcGFyYXMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcCA9IHRlbXBbLDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcSA9IHRlbXBbLDJdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTSA9IHRlbXBbLDNdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GKQ0KYXBwc19iYXNzX3BhcmFzW29yZGVyKGFwcHNfYmFzc19wYXJhcyRhcHBzKSxdDQpgYGANCg0KIyMgKipRdWVzdGlvbiAyKioNCj4gVGhpcyBxdWVzdGlvbiB1c2VzICJhZC50cmlhbCIgYXMgdGhlIGRhdGEuICANCg0KIyMjICoqUXVlc3Rpb24gMmEqKiANCj4gRm9yIHRoaXMgcXVlc3Rpb24sIHdlIG9ubHkgbmVlZCB0byBydW4gdGhyZWUgcmVncmVzc2lvbnMgd2l0aCB0aGUgdHJlYXRtZW50IHZhcmlhYmxlIHVzaW5nIGRpZmZlcmVudCBvdXRjb21lcy4gQXMgbm90ZWQgaW4gdGhlIHF1ZXN0aW9uLCB3ZSBuZWVkIHRvIHRyYW5zZm9ybSB0aGUgdmFyaWFibGVzIGluIHRoZSBlc3RpbWF0aW9ucy4gDQoNClRoZSBBVEUgb24gcGFnZSB2aWV3czoNCg0KYGBge3J9DQpwYWdlX3ZpZXdzIDwtIGxtKGxvZyhwYWdldmlld3MpfnRyZWF0bWVudCxkYXRhID0gYWQudHJpYWwpDQpzdW1tYXJ5KHBhZ2Vfdmlld3MpDQpgYGANCg0KVGhlIEFURSBvbiByZXNlcnZhdGlvbnM6IA0KYGBge3J9DQpyZXNlcnZhdGlvbnMgPC0gbG0obG9nKHJlc2VydmF0aW9ucyl+dHJlYXRtZW50LGRhdGEgPSBhZC50cmlhbCkNCnN1bW1hcnkocmVzZXJ2YXRpb25zKQ0KYGBgDQoNClRoZSBBVEUgb24gY29udmVyc2lvbiByYXRlcyAoQ1IpOg0KYGBge3J9DQojIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBvZiBjb252ZXJzaW9uIHJhdGUNCmFkLnRyaWFsJENSIDwtIGFkLnRyaWFsJHJlc2VydmF0aW9ucy9hZC50cmlhbCRwYWdldmlld3MNCkNSIDwtIGxtKGxvZyhDUi8oMS1DUikpfnRyZWF0bWVudCxkYXRhID0gYWQudHJpYWwpDQpzdW1tYXJ5KENSKQ0KYGBgDQoNCiMjIyAqKlF1ZXN0aW9uIDJjLjEqKiANCj4gRm9yIHRoaXMgcXVlc2l0b24sIHdlIHJ1biByZWdyZXNzaW9ucyB3aGljaCBpbnRlcmFjdCB0aGUgdHlwZXMgb2YgcmVzdGF1cmFudHMgKGFkLnRyaWFsOnJlc3RhdXJhbnRfdHlwZSkgYW5kIHRoZSB0cmVhdG1lbnQgdmFyaWFibGUgKGFkLnRyaWFsOnRyZWF0bWVudCkuIA0KDQpUaGUgaGV0ZXJvZ2VuZW91cyB0cmVhdG1lbnQgZWZmZWN0cyBvbiBwYWdlIHZpZXdzOg0KYGBge3J9DQpoZXRlcm9fcGFnZV92aWV3cyA8LSBsbShsb2cocGFnZXZpZXdzKX50cmVhdG1lbnQqcmVzdGF1cmFudF90eXBlLGRhdGEgPSBhZC50cmlhbCkNCnN1bW1hcnkoaGV0ZXJvX3BhZ2Vfdmlld3MpDQoNCmBgYA0KDQpUaGUgaGV0ZXJvZ2VuZW91cyB0cmVhdG1lbnQgZWZmZWN0cyBvbiByZXNlcnZhdGlvbnM6DQpgYGB7cn0NCmhldGVyb19yZXNlcnZhdGlvbnMgPC0gbG0obG9nKHJlc2VydmF0aW9ucyl+dHJlYXRtZW50KnJlc3RhdXJhbnRfdHlwZSxkYXRhID0gYWQudHJpYWwpDQpzdW1tYXJ5KGhldGVyb19yZXNlcnZhdGlvbnMpDQpgYGANCg0KIyMgKipRdWVzdGlvbiAzKioNCj4gVGhpcyBxdWVzdGlvbiB1c2VzICJhaXJibmIiIGFzIHRoZSBkYXRhLg0KDQojIyMgKipRdWVzaXRvbiAzYSoqDQo+IEhlcmUgd2UgdHJ5IHRvIHNlbGVjdCB0aGUgIm9wdGltYWwiIGJhbmR3aWR0aCB3aXRoIHRoZSAyLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBtZXRob2QuIFRoZSBvcHRpbWFsIGJhbmR3aWR0aCBpcyB0aGUgb25lIHRoYXQgaGFzIHRoZSBzbWFsbGVzdCBtZWFuIHNxdWFyZWQgZXJyb3IgKE1TRSkgb24gdGhlIHRlc3Rpbmcgc2FtcGxlLiBXZSB0ZXN0IDUgYmFuZHdpZHRoLiANCj4gVG8gc2VsZWN0IHRoZSBvcHRpbWFsIGJhbmR3aWR0aCwgd2UgYWRvcHQgYSByZWdyZXNzaW9uLWJhc2VkIGVzdGltYXRvciBvZiBuZWlnaGJvcmhvb2QgZWZmZWN0cy4gV2UgZXN0aW1hdGUgYSByZWdyZXNzaW9uIGVxdWF0aW9uIGxpa2UgdGhpczogJFByaWNlID0gTmVpZ2hib3VyaG9vZCArIERpc3RhbmNlICsgTmVpZ2hib3VyaG9vZFx0aW1lcyBEaXN0YW5jZSArIGUkLiANCg0KYGBge3J9DQojPT09PT0gdG8gc3BsaXQgZGF0YSBpbnRvIHRlc3QgYW5kIHRyYWluIHNldCAtLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIFNldCBzZWVkcyBmb3IgcmVwbGljYXRpb24gDQpzZXQuc2VlZCgxMjM0NSkgDQoNCiMgUGVybXV0YXRlIHRoZSBpbmRleA0KTiA8LSBkaW0oYWlyYm5iKVsxXQ0KaWR4IDwtIHNhbXBsZSgxOk4sTixyZXBsYWNlID0gRikNCg0KIyBUbyBvYnRhaW4gYSByYW5kb20gc3Vic2FtcGxlIGFzIHRyYWluIGFuZCB0ZXN0IGRhdGENCnRyYWluIDwtIGFpcmJuYltpZHhbMTooTi8yKV0sXQ0KdGVzdCA8LSBhaXJibmJbaWR4WyhOLzIrMSk6Tl0sXQ0KDQojPT09PT0gVG8gc2VsZWN0IG9wdGltYWwgYmFuZHdpZHRocyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgSGVyZSB3ZSB0ZXN0IDUgYmFuZHdpZHRocy4gDQoNCiMgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGRpc3RhbmNlDQpIIDwtIHJlcChzZChhaXJibmIkZGlzdGFuY2UpLDUpKnNlcSgwLjEsMC45LDAuMikNCg0KIyBhIHZlY3RvciB0byBzdG9yZSBNU0UgDQpILmZpdCA8LSByZXAoMCw1KQ0KDQojIExvb3Agb3ZlciA1IGJhbmR3aWR0aHMNCmZvciAoaSBpbiAxOjUpIHsNCiAgDQogICMgVGhpcyBpcyB0aGUgbW9kZWwgc3BlY2lmaWVkIGFib3ZlLiBNb3JlIGNvbXBsZXggbWV0aG9kIG1heSBiZSB1c2VkIGhlcmUuIA0KICBtZGwgPC0gbG0ocHJpY2V+bmVpZ2hib3VyaG9vZCpkaXN0YW5jZSx0cmFpblthYnModHJhaW4kZGlzdGFuY2UpPEhbaV0sXSkNCiAgDQogICMgVG8gb2J0YWluIHRoZSBwcmVkaWN0ZWQgb3V0Y29tZSBmb3IgdGVzdCBkYXRhLiANCiAgcHJpY2UuaGF0IDwtIHByZWRpY3QobWRsLG5ld2RhdGEgPSB0ZXN0W2Ficyh0ZXN0JGRpc3RhbmNlKTxIW2ldLF0pDQogIA0KICAjIFRvIGNhbGN1YWx0ZSBhbmQgc3RvcmUgTVNFDQogIEguZml0W2ldIDwtIG1lYW4oKHRlc3QkcHJpY2VbYWJzKHRlc3QkZGlzdGFuY2UpPEhbaV1dLXByaWNlLmhhdCleMikNCn0NCg0KIyBUbyBnZXQgdGhlIGJhbmR3aWR0aCBvZiB0aGUgb25lIHdpdGggbWluaW51bSBNU0UNCkggPC0gSFtILmZpdD09bWluKEguZml0KV0NCg0KIyBUaGUgTVNFIG9mIHRoZSA1IGJhbmR3aWR0aHM6DQpILmZpdA0KDQojVGhlIG9wdGltYWwgYmFuZHdpZHRoIGlzOg0KSA0KDQpgYGANCg0KIyMjICoqUXVlc3Rpb24gM2IuMSoqDQo+IEFmdGVyIGhhdmluZyB0aGUgb3B0aW1hbCBiYW5kd2lkdGggJEgkLCB3ZSByZXJ1biB0aGUgbW9kZWwgd2l0aCB0aGUgZnVsbCBkYXRhLiBUaGlzIGdpdmVzIHVzIGEgImxvY2FsIGF2ZXJhZ2UgdHJlYXRtZW50IGVmZmVjdHMiIChMQVRFKSwgaS5lLiwgdGhlIG5laWdoYm9yaG9vZCBlZmZlY3RzIG9uIHByaWNlcyBmb3IgaG90ZWxzIGxvY2F0ZWQgY2xvc2UgdG8gdGhlICJib2FyZGVyIiBvZiB0aGUgdHdvIG5laWdoYm9yaG9vZHMuIA0KDQpgYGB7cn0NCiNSZXJ1biBhIG1vZGVsIG9uIGZ1bGwgZGF0YSB3aXRoIGJhbmR3aWR0aCBzZXQgdG8gSA0KbWRsIDwtIGxtKHByaWNlfm5laWdoYm91cmhvb2QqZGlzdGFuY2UsYWlyYm5iW2FicyhhaXJibmIkZGlzdGFuY2UpPEgsXSkNCg0KIyBUaGUgbmVpZ2hib3Job29kIGVmZmVjdCBpcyBtZWFzdXJlZCBieSB0aGUgY29lZmZpY2llbnQgb2YgdGhlIG5laWdoYm9yaG9vZCBkdW1teS4NCnN1bW1hcnkobWRsKQ0KYGBgDQoNCiMjIyAqKlF1ZXN0aW9uIDNiLjIqKg0KPiBUbyByZXJ1biB0aGUgbW9kZWwgd2l0aCBjb250cm9sIHZhcmlhYmxlcyBhZGRlZCB0byBjaGVjayB0aGUgcm9idXN0bmVzcyBvZiB0aGUgTEFURS4gDQoNCmBgYHtyfQ0KI1JlcnVuIGEgbW9kZWwgb24gZnVsbCBkYXRhIHdpdGggYmFuZHdpZHRoIHNldCB0byBIDQptZGwuMSA8LSBsbShwcmljZX5uZWlnaGJvdXJob29kKmRpc3RhbmNlICsgDQogICAgICAgICAgICByb29tX3R5cGUgKyANCiAgICAgICAgICAgIHJldmlld3NfcGVyX21vbnRoICsgDQogICAgICAgICAgICByZXZpZXdfc2NvcmVzX3JhdGluZyArIA0KICAgICAgICAgICAgaG9zdF9pc19zdXBlcmhvc3QsYWlyYm5iW2FicyhhaXJibmIkZGlzdGFuY2UpPEgsXSkNCg0KIyBUaGUgbmVpZ2hib3Job29kIGVmZmVjdCBpcyBtZWFzdXJlZCBieSB0aGUgY29lZmZpY2llbnQgb2YgdGhlIG5laWdoYm9yaG9vZCBkdW1teS4NCnN1bW1hcnkobWRsLjEpDQpgYGANCg0K