Chapter 1: An Overview of the R Language

library(lavaan)
library(semPlot)
library(corrplot)
library(multcomp)

Load Test Data

satData <- read.csv('sat.csv')
satData$Segment <- factor(satData$Segment)
head(satData)
  iProdSAT iSalesSAT Segment iProdREC iSalesREC
1        6         2       1        4         3
2        4         5       3        4         4
3        5         3       4        5         4
4        3         3       2        4         4
5        3         3       3        2         2
6        4         4       4        5         4
summary(satData)
    iProdSAT      iSalesSAT     Segment
 Min.   :1.00   Min.   :1.000   1: 54  
 1st Qu.:3.00   1st Qu.:3.000   2:131  
 Median :4.00   Median :4.000   3:154  
 Mean   :4.13   Mean   :3.802   4:161  
 3rd Qu.:5.00   3rd Qu.:5.000          
 Max.   :7.00   Max.   :7.000          
    iProdREC       iSalesREC    
 Min.   :1.000   Min.   :1.000  
 1st Qu.:3.000   1st Qu.:3.000  
 Median :4.000   Median :3.000  
 Mean   :4.044   Mean   :3.444  
 3rd Qu.:5.000   3rd Qu.:4.000  
 Max.   :7.000   Max.   :7.000  
corrplot.mixed(cor(satData[, -3]))

Does product satisfaction differ by segment?

aggregate(iProdSAT ~ Segment, satData, mean)
  Segment iProdSAT
1       1 3.462963
2       2 3.725191
3       3 4.103896
4       4 4.708075

Are the difference statistically significant?

Segment 4 has the highest level of satisfaction, but are the differences statistically significant? We perform a one way analysis of variance (ANOVA) and see that satisfaction differs significantly by segment:

sat.anova <- aov(iProdSAT ~ -1 + Segment, satData)
summary(sat.anova)
           Df Sum Sq Mean Sq F value Pr(>F)    
Segment     4   8628    2157    2161 <2e-16 ***
Residuals 496    495       1                   
---
Signif. codes:  
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

We plot the ANOVA model to visualize confidence intervals for mean product sat- isfaction by segment:

  par(mar=c(4,8,4,2))
  plot(glht(sat.anova))

Structual Equation Model to the Satisfaction Data

We propose that the SAT latent variable is manifested in the two satisfaction items, while REC is manifested in the two likelihood-to-recommend items. As marketers, we expect and hope that the latent likelihood-to-recommend variable (REC) would be affected by the latent satisfaction (SAT).
satModel <- "SAT =~ iProdSAT + iSalesSAT
            REC =~ iProdREC + iSalesREC
            REC~ SAT"
- This model might be paraphrased as “Latent SATisfaction is observed as items iProdSAT and iSalesSAT. Latent likelihood to RECommend is observed as items iProdREC and iSalesREC. RECommendation varies with SATisfaction”.
sat.fit <- cfa(satModel, data=satData)
Found more than one class "Model" in cache; using the first, from namespace 'lavaan'
summary(sat.fit, fit.m=TRUE)
lavaan (0.5-20) converged normally after  31 iterations

  Number of observations                           500

  Estimator                                         ML
  Minimum Function Test Statistic                2.319
  Degrees of freedom                                 1
  P-value (Chi-square)                           0.128

Model test baseline model:

  Minimum Function Test Statistic              278.557
  Degrees of freedom                                 6
  P-value                                        0.000

User model versus baseline model:

  Comparative Fit Index (CFI)                    0.995
  Tucker-Lewis Index (TLI)                       0.971

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)              -3040.385
  Loglikelihood unrestricted model (H1)      -3039.225

  Number of free parameters                          9
  Akaike (AIC)                                6098.769
  Bayesian (BIC)                              6136.701
  Sample-size adjusted Bayesian (BIC)         6108.134

Root Mean Square Error of Approximation:

  RMSEA                                          0.051
  90 Percent Confidence Interval          0.000  0.142
  P-value RMSEA <= 0.05                          0.347

Standardized Root Mean Square Residual:

  SRMR                                           0.012

Parameter Estimates:

  Information                                 Expected
  Standard Errors                             Standard

Latent Variables:
                   Estimate  Std.Err  Z-value
  SAT =~                                     
    iProdSAT          1.000                  
    iSalesSAT         1.067    0.173    6.154
  REC =~                                     
    iProdREC          1.000                  
    iSalesREC         0.900    0.138    6.528
  P(>|z|)
         
         
    0.000
         
         
    0.000

Regressions:
                   Estimate  Std.Err  Z-value
  REC ~                                      
    SAT               0.758    0.131    5.804
  P(>|z|)
         
    0.000

Variances:
                   Estimate  Std.Err  Z-value
    iProdSAT          0.706    0.088    7.994
    iSalesSAT         0.793    0.100    7.918
    iProdREC          0.892    0.129    6.890
    iSalesREC         0.808    0.107    7.533
    SAT               0.483    0.097    4.970
    REC               0.516    0.115    4.505
  P(>|z|)
    0.000
    0.000
    0.000
    0.000
    0.000
    0.000

We visualize the structural model using the semPlot package:

The model converged and reported many statistics that we omit above, but we note that the model fits the data well with a Comparative Fit Index near 1.0

semPaths(sat.fit, what="est",
          residuals=FALSE, intercepts=FALSE, nCharNodes=9)

This produces the chart shown in Fig. 2.4. Each proposed latent variable is highly loaded on its manifested (observed) survey items. With an estimated coefficient of 0.76, customers’ latent satisfaction is shown to have a strong association with their likelihood to recommend.

2.4.3 More on Vectors and Indexing

xSeq <- 1:(4*2)
xSeq
[1] 1 2 3 4 5 6 7 8
xSeq <- 1:4*2
xSeq
[1] 2 4 6 8
# Sequences are useful for indexing and you can use sequences inside [ ]:
1:300
  [1]   1   2   3   4   5   6   7   8   9  10  11
 [12]  12  13  14  15  16  17  18  19  20  21  22
 [23]  23  24  25  26  27  28  29  30  31  32  33
 [34]  34  35  36  37  38  39  40  41  42  43  44
 [45]  45  46  47  48  49  50  51  52  53  54  55
 [56]  56  57  58  59  60  61  62  63  64  65  66
 [67]  67  68  69  70  71  72  73  74  75  76  77
 [78]  78  79  80  81  82  83  84  85  86  87  88
 [89]  89  90  91  92  93  94  95  96  97  98  99
[100] 100 101 102 103 104 105 106 107 108 109 110
[111] 111 112 113 114 115 116 117 118 119 120 121
[122] 122 123 124 125 126 127 128 129 130 131 132
[133] 133 134 135 136 137 138 139 140 141 142 143
[144] 144 145 146 147 148 149 150 151 152 153 154
[155] 155 156 157 158 159 160 161 162 163 164 165
[166] 166 167 168 169 170 171 172 173 174 175 176
[177] 177 178 179 180 181 182 183 184 185 186 187
[188] 188 189 190 191 192 193 194 195 196 197 198
[199] 199 200 201 202 203 204 205 206 207 208 209
[210] 210 211 212 213 214 215 216 217 218 219 220
[221] 221 222 223 224 225 226 227 228 229 230 231
[232] 232 233 234 235 236 237 238 239 240 241 242
[243] 243 244 245 246 247 248 249 250 251 252 253
[254] 254 255 256 257 258 259 260 261 262 263 264
[265] 265 266 267 268 269 270 271 272 273 274 275
[276] 276 277 278 279 280 281 282 283 284 285 286
[287] 287 288 289 290 291 292 293 294 295 296 297
[298] 298 299 300
  

2.4.5 Missing and Interesting Values

my.test.scores <- c(91, NA, NA)
mean(my.test.scores)
[1] NA
max(my.test.scores)
[1] NA
mean(my.test.scores, na.rm = TRUE)
[1] 91
max(my.test.scores, na.rm = TRUE)
[1] 91
# A second approach is to remove NA values explicitly before calculating on them or assigning them elsewhere. This may be done most easily with the command na.omit():
mean(na.omit(my.test.scores))
[1] 91
# A third and more cumbersome alternative is to test for NA using the is.na() function, and then index data for the values that are not NA by adding the ! (“not”) operator:
is.na(my.test.scores)
[1] FALSE  TRUE  TRUE
my.test.scores[!is.na(my.test.scores)]
[1] 91
# One thing never to do in R is to use an actual numeric value such as −999 to in- dicate missing data. That will cause headaches at best and wrong answers at worst. Instead, as soon as you load such data into R, replace those values with NA using indices:
my.test.scores <- c(91, -999, -999)
mean(my.test.scores)
[1] -635.6667
my.test.scores[my.test.scores < -900] <- NA
mean(my.test.scores, na.rm=TRUE)
[1] 91
log(c(-1,0,1))
Warning in log(c(-1, 0, 1)) : NaNs produced
[1]  NaN -Inf    0
# We get a warning because log() is undefined for negative numbers and log(-1) gives a value of NaN. Note also that log(0) = −∞ (-Inf).

2.4.7 Lists

xNum  <- c(1, 3.14159, 5, 7)
xLog  <- c(TRUE, FALSE, TRUE, TRUE)
xChar <- c("foo", "bar", "boo", "far")
xMix  <- c(1, TRUE, 3, "Hello, world!")
xList <- list(xNum, xChar)
xList
[[1]]
[1] 1.00000 3.14159 5.00000 7.00000

[[2]]
[1] "foo" "bar" "boo" "far"
str(xNum)
 num [1:4] 1 3.14 5 7
summary(xList[[1]])
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.000   2.606   4.071   4.035   5.500   7.000 

It is often more convenient to run such a command on all members of the list at once. We can do that with the lapply() or “list apply” command.

With lapply() we must pay special attention to the argument order: lapply (OBJECT,FUNCTION).We use lapply() to produce a summary() for each member of the list:

lapply(xList, summary)
[[1]]
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.000   2.606   4.071   4.035   5.500   7.000 

[[2]]
   Length     Class      Mode 
        4 character character 

Using lapply() to iterate in this way saves a lot of work, especially with lists that may comprise dozens or hundreds of objects. It demonstrates that lists have two advantages: they keep data in one place regardless of constituent types, and they make it possible to apply operations automatically to diverse parts of that data.

2.5 Data Frames

Data frames are the workhorse objects in R, used to hold data sets and to provide data to statistical functions and models. A data frame’s general structure will be fa- miliar to any analyst: it is a rectangular object comprised of columns of varying data types (often referred to as “variables”) and rows that each has a value (or missing value, NA) in each column (“observations”).

x.df <- data.frame(xNum, xLog, xChar)
x.df
     xNum  xLog xChar
1 1.00000  TRUE   foo
2 3.14159 FALSE   bar
3 5.00000  TRUE   boo
4 7.00000  TRUE   far
# [ROW, COLUMN]notation:
x.df[2,1]
[1] 3.14159
x.df[1,3]
[1] foo
Levels: bar boo far foo

by default R converts character data in data frames to nominal factors.

# 
# Nominal 
# Ordinal - less/greater
# Interval 
# Ratio - true 0

Converting character strings to factors is a good thing for data that you might use in a statistical model because it tells R to handle it appropriately in the model, but it’s inconvenient when the data really is simple text such as an address or com- ments on a survey. You can prevent the conversion to factors by adding an option to data.frame() that sets stringsAsFactors=FALSE:

x.df <- data.frame(xNum, xLog, xChar, stringsAsFactors=FALSE)
x.df
     xNum  xLog xChar
1 1.00000  TRUE   foo
2 3.14159 FALSE   bar
3 5.00000  TRUE   boo
4 7.00000  TRUE   far
x.df[1,3]
[1] "foo"
# all obs
x.df[2, ]
     xNum  xLog xChar
2 3.14159 FALSE   bar
#all columns
x.df[,2 ]
[1]  TRUE FALSE  TRUE  TRUE
x.df[ ,3]  # all of column 3
[1] "foo" "bar" "boo" "far"
# Index data frames by using vectors or ranges for the elements you want. Use nega- tive indices to omit elements:
x.df[2:3, ]
     xNum  xLog xChar
2 3.14159 FALSE   bar
3 5.00000  TRUE   boo
print(x.df[ ,1:2]) # two columns
     xNum  xLog
1 1.00000  TRUE
2 3.14159 FALSE
3 5.00000  TRUE
4 7.00000  TRUE
x.df[-3, ] # omit the third observation
     xNum  xLog xChar
1 1.00000  TRUE   foo
2 3.14159 FALSE   bar
4 7.00000  TRUE   far
x.df[, -2]  # omit the second column
     xNum xChar
1 1.00000   foo
2 3.14159   bar
3 5.00000   boo
4 7.00000   far

Let’s create a new data set that is more representative of data in marketing research. We’ll clean up our workspace and then create new data:

rm(list=ls())     # caution, deletes all objects! See explanation below
store.num <- factor(c(3, 14, 21, 32, 54)) # store id
store.rev <- c(543, 654, 345, 678, 234)   # store revenue, $1000
store.visits <- c(45, 78, 32, 56, 34)     # visits, 1000s
store.manager <- c("Annie", "Bert", "Carla", "Dave", "Ella")
(store.df <- data.frame(store.num, store.rev, store.visits, store.manager, stringsAsFactors=F)) # F = FALSE
  store.num store.rev store.visits store.manager
1         3       543           45         Annie
2        14       654           78          Bert
3        21       345           32         Carla
4        32       678           56          Dave
5        54       234           34          Ella
# Pearson's r
cor(store.df$store.rev, store.df$store.visits)
[1] 0.8291032
summary(store.df)
 store.num   store.rev      store.visits
 3 :1      Min.   :234.0   Min.   :32   
 14:1      1st Qu.:345.0   1st Qu.:34   
 21:1      Median :543.0   Median :45   
 32:1      Mean   :490.8   Mean   :49   
 54:1      3rd Qu.:654.0   3rd Qu.:56   
           Max.   :678.0   Max.   :78   
 store.manager     
 Length:5          
 Class :character  
 Mode  :character  
                   
                   
                   

2.6 Loading and Saving Data

save(store.df, file="store-df-backup.RData")
rm(store.df)
# mean(store.df$store.rev)
 
load("store-df-backup.RData")
mean(store.df$store.rev)
[1] 490.8
# save() can also take a group of objects as an argument; just replace the single object name with list=c() and fill in c() with a character vector. For in- stance:
save(list=c("store.df","store.visits"), file="store-df-backup.RData")

2.7 Writing Your Own Functions

Many analyses in R are repetitive: compute statistics across slices of data such as different sales regions, produce analyses from new data sets such as successive cal- endar quarters, and so forth. R provides functions to let you write a set of commands once and reuse it with new data.

se <- function(x) { sd(x) / sqrt(length(x)) }
se(store.df$store.visits)
[1] 8.42615

A function’s results can also be assigned to other variables or used in additional functions. For example, we might compute the upper-bound 95 % confidence interval as the mean + 1.96 standard error:

mean(store.df$store.visits) + 1.96 * se(store.df$store.visits)
[1] 65.51525

This tells us that, if the present data are a good random sample from a larger set, we could expect the mean of other such samples to be 65.51 or less in 97.5 % of the samples (97.5 % because the 95 % confidence interval is symmetric around 50 %, extending from 2.5 to 97.5 %). In other words, we can be highly confident from these data that the mean number of store visits is less than 65.52.

Aschematic for a new function is: FUNCTIONNAME <- function(INPUTS) EXPR . In most cases, EXPR is a set of multiple lines that operate on the inputs. When there are multiple lines, they must be enclosed with braces { and }. By default, the return value of the function is the output of the last command in the function declaration.

4 conventions to writing functions:

• Put braces around the body using { and }, even if it’s just a one line function • Create temporary values to hold results along the way inside the function • Comment the function profusely • Use the keyword return() to show the explicit value returned by the function.

se <- function(x) {
  # computes standard error of the mean
  tmp.sd <- sd(x)      # standard deviation
  tmp.N  <- length(x)  # sample size
  tmp.se <- tmp.sd / sqrt(tmp.N)   # std error of the mean
  return(tmp.se)
}

2.7.1 Language Structures

If you program in a language such as C or Java, the control structures in R will be familiar. Using TEST to indicate a Boolean value (or value coercible to Boolean) and EXPR for any language expression—which may include a block of expressions inside { and }—R provides:

# EXPR = expression()
# if (TEST) EXPR [else EXPR.b]  # do EXPR if TEST is true, else EXPR.b
# while (TEST) EXPR 
# 
# for (NAME in VECTOR) EXPR # iterate expr for values of name from vector
# 
# switch(INDEX, LIST) # INDEXth statement or matching argument name from LIST
# type <- 'trimmed'
# switch(type,
#   mean = mean(x),
#   median = median(x),
#   trimmed = mean(x, trim = .1))
# repeat EXPR # repeats forever until break; not recommended

Vectorised version of if statement called ifelse(), checks if element on a condition and converts it to a true or false then transforms it based on your input.

?switch
library(devtools)
x <- -2:2
log(x)
Warning in log(x) : NaNs produced
[1]       NaN       NaN      -Inf 0.0000000
[5] 0.6931472
ifelse(x > 0, x, NA)
[1] NA NA NA  1  2
log(ifelse(x > 0, x, NA))
[1]        NA        NA        NA 0.0000000
[5] 0.6931472

2.7.2 Anonymous Functions

also known as a lambda expression

apply works for non-lists like vectors/dataframes

my.data <- matrix(runif(100), ncol = 5) # 100 rand nums in 5 cols
median(my.data[,1])/2
[1] 0.2877563
apply(my.data, 2, median) / 2
[1] 0.2877563 0.2235877 0.2258379 0.2197439
[5] 0.1710344

2.8 Clean Up

ls() # to see what you have in memory
[1] "my.data"       "se"            "store.df"     
[4] "store.manager" "store.num"     "store.rev"    
[7] "store.visits"  "x"            
rm() #to remove the object
rm(list=ls())

2.10 Key Points

Most of the present chapter is foundational to R, yet there are a few especially important points:

  • • For work that you want to preserve or edit, use a text editor and run commands from there (Sect. 2.3).

  • • Create vectors using c() for enumerated values, seq() for sequences, and rep() for repeated values (Sects. 2.4.1 and 2.4.3).

  • • Use the constant NA for missing values, not an arbitrary value such as −999 (Sect. 2.4.5).

  • • In R, data sets are most commonly data.frame objects created with a command such as my.df <- data.frame(vector1, vector2, …) (Sect. 2.5) or by reading a data file.

  • • Vectors and data frames are most often indexed with specific numbers (x[1]), ranges (x[2:4]), negative indices (x[-3]) to omit data, and by boolean selection (x[x>3]) (Sects. 2.5 and 2.4.3).

  • • Data frames are indexed by[ROW,COLUMN], where a blank value means “all of that dimension” such as my.df[2,] for row2, all columns(Sect.2.5).

  • • You can also index a data frame with $ and a column name, such as my.df$id (Sect. 2.5).

  • • Read and write data in CSV format with read.csv() and write.csv() (Sect. 2.6.2).

  • • Functions are straightforward to write and extend R’s capabilities. When you write a function, organize the code well and comment it profusely (Sect. 2.7).

  • • Cleanupyourworkspaceregularlytoavoidclutterandbugsfromobsoletevari- ables (Sect. 2.8).

Part II - Fundamentals of Data Analysis

3. Describe Data

3.1 Simulating Data

3.1.1

Our first data set represents observations of total sales by week for two products at a chain of stores. We begin by creating a data structure that will hold the data, a simulation of sales for the two products in 20 stores over 2 years, with price and pro- motion status. We remove most of the R output here to focus on the input commands.

k.stores <- 20 # 20 stores, using "k." for "constant"
k.weeks <- 104    # 2 years of data each
# created a data frame of initially missing values to hold the data
store.df <- data.frame(matrix(NA, ncol=10, nrow=k.stores*k.weeks))
names(store.df) <- c("storeNum", "Year", "Week", "p1sales", "p2sales","p1price", "p2price", "p1prom", "p2prom","country")
dim(store.df)
[1] 2080   10
# we create two vectors that will represent the store number and country for each observation
store.num <- 101:(100+k.stores)
(store.cty <- c(rep("US", 3), rep("DE", 5), rep("GB", 3), rep("BR", 2), rep("JP", 4), rep("AU", 1), rep("CN", 2)))
 [1] "US" "US" "US" "DE" "DE" "DE" "DE" "DE" "GB"
[10] "GB" "GB" "BR" "BR" "JP" "JP" "JP" "JP" "AU"
[19] "CN" "CN"
length(store.cty) 
[1] 20
# now we replace the appropiate columns in the data frame with those values using rep() to expand the vectors to match the number of stores and weeks
store.df$storeNum <- rep(store.num, each = k.weeks)
store.df$country <- rep(store.cty, each = k.weeks)
rm(store.num, store.cty)    # clean up
str(store.df)
'data.frame':   2080 obs. of  10 variables:
 $ storeNum: int  101 101 101 101 101 101 101 101 101 101 ...
 $ Year    : logi  NA NA NA NA NA NA ...
 $ Week    : logi  NA NA NA NA NA NA ...
 $ p1sales : logi  NA NA NA NA NA NA ...
 $ p2sales : logi  NA NA NA NA NA NA ...
 $ p1price : logi  NA NA NA NA NA NA ...
 $ p2price : logi  NA NA NA NA NA NA ...
 $ p1prom  : logi  NA NA NA NA NA NA ...
 $ p2prom  : logi  NA NA NA NA NA NA ...
 $ country : chr  "US" "US" "US" "US" ...
# check the types, notice the chr type vectors, if they are discrete values like 'country' then they should be "factored" using factor(), storeNum is a label so we can treat it as a factor as it isn't a "number" in the numerical sense(not for calculation) of the word.
#next we do the same to the week and year
(store.df$Week <- rep(1:52, times = k.stores*2))
   [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
  [16] 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
  [31] 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
  [46] 46 47 48 49 50 51 52  1  2  3  4  5  6  7  8
  [61]  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
  [76] 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
  [91] 39 40 41 42 43 44 45 46 47 48 49 50 51 52  1
 [106]  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16
 [121] 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 [136] 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
 [151] 47 48 49 50 51 52  1  2  3  4  5  6  7  8  9
 [166] 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 [181] 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
 [196] 40 41 42 43 44 45 46 47 48 49 50 51 52  1  2
 [211]  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17
 [226] 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 [241] 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
 [256] 48 49 50 51 52  1  2  3  4  5  6  7  8  9 10
 [271] 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 [286] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
 [301] 41 42 43 44 45 46 47 48 49 50 51 52  1  2  3
 [316]10  5  6  7  8  9 10 11 12 13 14 15 16 17 18
 [331] 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 [346] 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
 [361] 49 50 51 52  1  2  3  4  5  6  7  8  9 10 11
 [376] 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 [391] 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
 [406] 42 43 44 45 46 47 48 49 50 51 52  1  2  3  4
 [421]  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
 [436] 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 [451] 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
 [466] 50 51 52  1  2  3  4  5  6  7  8  9 10 11 12
 [481] 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 [496] 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
 [511] 43 44 45 46 47 48 49 50 51 52  1  2  3  4  5
 [526]  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
 [541] 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 [556] 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
 [571] 51 52  1  2  3  4  5  6  7  8  9 10 11 12 13
 [586] 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 [601] 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
 [616] 44 45 46 47 48 49 50 51 52  1  2  3  4  5  6
 [631]  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21
 [646] 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
 [661] 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
 [676] 52  1  2  3  4  5  6  7  8  9 10 11 12 13 14
 [691] 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 [706] 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
 [721] 45 46 47 48 49 50 51 52  1  2  3  4  5  6  7
 [736]  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22
 [751] 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
 [766] 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
 [781]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
 [796] 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 [811] 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
 [826] 46 47 48 49 50 51 52  1  2  3  4  5  6  7  8
 [841]  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 [856] 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
 [871] 39 40 41 42 43 44 45 46 47 48 49 50 51 52  1
 [886]  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16
 [901] 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 [916] 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
 [931] 47 48 49 50 51 52  1  2  3  4  5  6  7  8  9
 [946] 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 [961] 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
 [976] 40 41 42 43 44 45 46 47 48 49 50 51 52  1  2
 [991]  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17
[1006] 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
[1021] 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
[1036] 48 49 50 51 52  1  2  3  4  5  6  7  8  9 10
[1051] 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
[1066] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
[1081] 41 42 43 44 45 46 47 48 49 50 51 52  1  2  3
[1096]  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
[1111] 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
[1126] 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
[1141] 49 50 51 52  1  2  3  4  5  6  7  8  9 10 11
[1156] 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
[1171] 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
[1186] 42 43 44 45 46 47 48 49 50 51 52  1  2  3  4
[1201]  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
[1216] 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
[1231] 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
[1246] 50 51 52  1  2  3  4  5  6  7  8  9 10 11 12
[1261] 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
[1276] 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
[1291] 43 44 45 46 47 48 49 50 51 52  1  2  3  4  5
[1306]  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
[1321] 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
[1336] 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
[1351] 51 52  1  2  3  4  5  6  7  8  9 10 11 12 13
[1366] 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
[1381] 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
[1396] 44 45 46 47 48 49 50 51 52  1  2  3  4  5  6
[1411]  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21
[1426] 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
[1441] 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
[1456] 52  1  2  3  4  5  6  7  8  9 10 11 12 13 14
[1471] 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
[1486] 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
[1501] 45 46 47 48 49 50 51 52  1  2  3  4  5  6  7
[1516]  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22
[1531] 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
[1546] 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
[1561]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
[1576] 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
[1591] 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
[1606] 46 47 48 49 50 51 52  1  2  3  4  5  6  7  8
[1621]  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
[1636] 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
[1651] 39 40 41 42 43 44 45 46 47 48 49 50 51 52  1
[1666]  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16
[1681] 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
[1696] 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
[1711] 47 48 49 50 51 52  1  2  3  4  5  6  7  8  9
[1726] 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
[1741] 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
[1756] 40 41 42 43 44 45 46 47 48 49 50 51 52  1  2
[1771]  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17
[1786] 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
[1801] 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
[1816] 48 49 50 51 52  1  2  3  4  5  6  7  8  9 10
[1831] 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
[1846] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
[1861] 41 42 43 44 45 46 47 48 49 50 51 52  1  2  3
[1876]  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
[1891] 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
[1906] 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
[1921] 49 50 51 52  1  2  3  4  5  6  7  8  9 10 11
[1936] 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
[1951] 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
[1966] 42 43 44 45 46 47 48 49 50 51 52  1  2  3  4
[1981]  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
[1996] 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
[2011] 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
[2026] 50 51 52  1  2  3  4  5  6  7  8  9 10 11 12
[2041] 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
[2056] 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
[2071] 43 44 45 46 47 48 49 50 51 52
#try the inner parts of the next line to figure out how we use rep
(store.df$Year  <- rep(rep(1:2, each=k.weeks/2), times=k.stores))
   [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
  [24] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
  [47] 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
  [70] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
  [93] 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1
 [116] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [139] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2
 [162] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [185] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [208] 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [231] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [254] 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [277] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [300] 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
 [323] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [346] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2
 [369] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [392] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [415] 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [438] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [461] 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [484] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [507] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1
 [530] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [553] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2
 [576] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [599] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [622] 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [645] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [668] 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [691] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [714] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1
 [737] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [760] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2
 [783] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [806] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [829] 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [852] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [875] 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2
 [898] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [921] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1
 [944] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [967] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2
 [990] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1013] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1036] 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1059] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1082] 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2
[1105] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1128] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1
[1151] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1174] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1197] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1220] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1243] 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1266] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1289] 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
[1312] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1335] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1
[1358] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1381] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1404] 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1427] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1450] 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1473] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1496] 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2
[1519] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1542] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1
[1565] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1588] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1611] 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1634] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1657] 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1680] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1703] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2
[1726] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1749] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1
[1772] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1795] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1818] 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1841] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1864] 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1887] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[1910] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2
[1933] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[1956] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1
[1979] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[2002] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[2025] 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[2048] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[2071] 2 2 2 2 2 2 2 2 2 2
store.df$storeNum <- factor(store.df$storeNum)
 store.df$country  <- factor(store.df$country)
 
 
str(store.df)
'data.frame':   2080 obs. of  10 variables:
 $ storeNum: Factor w/ 20 levels "101","102","103",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ Year    : int  1 1 1 1 1 1 1 1 1 1 ...
 $ Week    : int  1 2 3 4 5 6 7 8 9 10 ...
 $ p1sales : logi  NA NA NA NA NA NA ...
 $ p2sales : logi  NA NA NA NA NA NA ...
 $ p1price : logi  NA NA NA NA NA NA ...
 $ p2price : logi  NA NA NA NA NA NA ...
 $ p1prom  : logi  NA NA NA NA NA NA ...
 $ p2prom  : logi  NA NA NA NA NA NA ...
 $ country : Factor w/ 7 levels "AU","BR","CN",..: 7 7 7 7 7 7 7 7 7 7 ...
#inspct data frame at first and last rows
head(store.df, 120)  # 120 rows is enough to check 2 stores; not shown
    storeNum Year Week p1sales p2sales p1price
1        101    1    1      NA      NA      NA
2        101    1    2      NA      NA      NA
3        101    1    3      NA      NA      NA
4        101    1    4      NA      NA      NA
5        101    1    5      NA      NA      NA
6        101    1    6      NA      NA      NA
7        101    1    7      NA      NA      NA
8        101    1    8      NA      NA      NA
9        101    1    9      NA      NA      NA
10       101    1   10      NA      NA      NA
11       101    1   11      NA      NA      NA
12       101    1   12      NA      NA      NA
13       101    1   13      NA      NA      NA
14       101    1   14      NA      NA      NA
15       101    1   15      NA      NA      NA
16       101    1   16      NA      NA      NA
17       101    1   17      NA      NA      NA
18       101    1   18      NA      NA      NA
19       101    1   19      NA      NA      NA
20       101    1   20      NA      NA      NA
21       101    1   21      NA      NA      NA
22       101    1   22      NA      NA      NA
23       101    1   23      NA      NA      NA
24       101    1   24      NA      NA      NA
25       101    1   25      NA      NA      NA
26       101    1   26      NA      NA      NA
27       101    1   27      NA      NA      NA
28       101    1   28      NA      NA      NA
29       101    1   29      NA      NA      NA
30       101    1   30      NA      NA      NA
31       101    1   31      NA      NA      NA
32       101    1   32      NA      NA      NA
33       101    1   33      NA      NA      NA
34       101    1   34      NA      NA      NA
35       101    1   35      NA      NA      NA
36       101    1   36      NA      NA      NA
37       101    1   37      NA      NA      NA
38       101    1   38      NA      NA      NA
39       101    1   39      NA      NA      NA
40       101    1   40      NA      NA      NA
41       101    1   41      NA      NA      NA
42       101    1   42      NA      NA      NA
43       101    1   43      NA      NA      NA
44       101    1   44      NA      NA      NA
45       101    1   45      NA      NA      NA
46       101    1   46      NA      NA      NA
47       101    1   47      NA      NA      NA
48       101    1   48      NA      NA      NA
49       101    1   49      NA      NA      NA
50       101    1   50      NA      NA      NA
51       101    1   51      NA      NA      NA
52       101    1   52      NA      NA      NA
53       101    2    1      NA      NA      NA
54       101    2    2      NA      NA      NA
55       101    2    3      NA      NA      NA
56       101    2    4      NA      NA      NA
57       101    2    5      NA      NA      NA
58       101    2    6      NA      NA      NA
59       101    2    7      NA      NA      NA
60       101    2    8      NA      NA      NA
61       101    2    9      NA      NA      NA
62       101    2   10      NA      NA      NA
63       101    2   11      NA      NA      NA
64       101    2   12      NA      NA      NA
65       101    2   13      NA      NA      NA
66       101    2   14      NA      NA      NA
67       101    2   15      NA      NA      NA
68       101    2   16      NA      NA      NA
69       101    2   17      NA      NA      NA
70       101    2   18      NA      NA      NA
71       101    2   19      NA      NA      NA
72       101    2   20      NA      NA      NA
73       101    2   21      NA      NA      NA
74       101    2   22      NA      NA      NA
75       101    2   23      NA      NA      NA
76       101    2   24      NA      NA      NA
77       101    2   25      NA      NA      NA
78       101    2   26      NA      NA      NA
79       101    2   27      NA      NA      NA
80       101    2   28      NA      NA      NA
81       101    2   29      NA      NA      NA
82       101    2   30      NA      NA      NA
83       101    2   31      NA      NA      NA
84       101    2   32      NA      NA      NA
85       101    2   33      NA      NA      NA
86       101    2   34      NA      NA      NA
87       101    2   35      NA      NA      NA
88       101    2   36      NA      NA      NA
89       101    2   37      NA      NA      NA
90       101    2   38      NA      NA      NA
91       101    2   39      NA      NA      NA
92       101    2   40      NA      NA      NA
93       101    2   41      NA      NA      NA
94       101    2   42      NA      NA      NA
95       101    2   43      NA      NA      NA
96       101    2   44      NA      NA      NA
97       101    2   45      NA      NA      NA
98       101    2   46      NA      NA      NA
99       101    2   47      NA      NA      NA
100      101    2   48      NA      NA      NA
101      101    2   49      NA      NA      NA
102      101    2   50      NA      NA      NA
103      101    2   51      NA      NA      NA
104      101    2   52      NA      NA      NA
105      102    1    1      NA      NA      NA
106      102    1    2      NA      NA      NA
107      102    1    3      NA      NA      NA
108      102    1    4      NA      NA      NA
109      102    1    5      NA      NA      NA
110      102    1    6      NA      NA      NA
111      102    1    7      NA      NA      NA
112      102    1    8      NA      NA      NA
113      102    1    9      NA      NA      NA
114      102    1   10      NA      NA      NA
115      102    1   11      NA      NA      NA
116      102    1   12      NA      NA      NA
117      102    1   13      NA      NA      NA
118      102    1   14      NA      NA      NA
119      102    1   15      NA      NA      NA
120      102    1   16      NA      NA      NA
    p2price p1prom p2prom country
1        NA     NA     NA      US
2        NA     NA     NA      US
3        NA     NA     NA      US
4        NA     NA     NA      US
5        NA     NA     NA      US
6        NA     NA     NA      US
7        NA     NA     NA      US
8        NA     NA     NA      US
9        NA     NA     NA      US
10       NA     NA     NA      US
11       NA     NA     NA      US
12       NA     NA     NA      US
13       NA     NA     NA      US
14       NA     NA     NA      US
15       NA     NA     NA      US
16       NA     NA     NA      US
17       NA     NA     NA      US
18       NA     NA     NA      US
19       NA     NA     NA      US
20       NA     NA     NA      US
21       NA     NA     NA      US
22       NA     NA     NA      US
23       NA     NA     NA      US
24       NA     NA     NA      US
25       NA     NA     NA      US
26       NA     NA     NA      US
27       NA     NA     NA      US
28       NA     NA     NA      US
29       NA     NA     NA      US
30       NA     NA     NA      US
31       NA     NA     NA      US
32       NA     NA     NA      US
33       NA     NA     NA      US
34       NA     NA     NA      US
35       NA     NA     NA      US
36       NA     NA     NA      US
37       NA     NA     NA      US
38       NA     NA     NA      US
39       NA     NA     NA      US
40       NA     NA     NA      US
41       NA     NA     NA      US
42       NA     NA     NA      US
43       NA     NA     NA      US
44       NA     NA     NA      US
45       NA     NA     NA      US
46       NA     NA     NA      US
47       NA     NA     NA      US
48       NA     NA     NA      US
49       NA     NA     NA      US
50       NA     NA     NA      US
51       NA     NA     NA      US
52       NA     NA     NA      US
53       NA     NA     NA      US
54       NA     NA     NA      US
55       NA     NA     NA      US
56       NA     NA     NA      US
57       NA     NA     NA      US
58       NA     NA     NA      US
59       NA     NA     NA      US
60       NA     NA     NA      US
61       NA     NA     NA      US
62       NA     NA     NA      US
63       NA     NA     NA      US
64       NA     NA     NA      US
65       NA     NA     NA      US
66       NA     NA     NA      US
67       NA     NA     NA      US
68       NA     NA     NA      US
69       NA     NA     NA      US
70       NA     NA     NA      US
71       NA     NA     NA      US
72       NA     NA     NA      US
73       NA     NA     NA      US
74       NA     NA     NA      US
75       NA     NA     NA      US
76       NA     NA     NA      US
77       NA     NA     NA      US
78       NA     NA     NA      US
79       NA     NA     NA      US
80       NA     NA     NA      US
81       NA     NA     NA      US
82       NA     NA     NA      US
83       NA     NA     NA      US
84       NA     NA     NA      US
85       NA     NA     NA      US
86       NA     NA     NA      US
87       NA     NA     NA      US
88       NA     NA     NA      US
89       NA     NA     NA      US
90       NA     NA     NA      US
91       NA     NA     NA      US
92       NA     NA     NA      US
93       NA     NA     NA      US
94       NA     NA     NA      US
95       NA     NA     NA      US
96       NA     NA     NA      US
97       NA     NA     NA      US
98       NA     NA     NA      US
99       NA     NA     NA      US
100      NA     NA     NA      US
101      NA     NA     NA      US
102      NA     NA     NA      US
103      NA     NA     NA      US
104      NA     NA     NA      US
105      NA     NA     NA      US
106      NA     NA     NA      US
107      NA     NA     NA      US
108      NA     NA     NA      US
109      NA     NA     NA      US
110      NA     NA     NA      US
111      NA     NA     NA      US
112      NA     NA     NA      US
113      NA     NA     NA      US
114      NA     NA     NA      US
115      NA     NA     NA      US
116      NA     NA     NA      US
117      NA     NA     NA      US
118      NA     NA     NA      US
119      NA     NA     NA      US
120      NA     NA     NA      US
tail(store.df, 120)  # make sure end looks OK too; not shown
     storeNum Year Week p1sales p2sales p1price
1961      119    2   37      NA      NA      NA
1962      119    2   38      NA      NA      NA
1963      119    2   39      NA      NA      NA
1964      119    2   40      NA      NA      NA
1965      119    2   41      NA      NA      NA
1966      119    2   42      NA      NA      NA
1967      119    2   43      NA      NA      NA
1968      119    2   44      NA      NA      NA
1969      119    2   45      NA      NA      NA
1970      119    2   46      NA      NA      NA
1971      119    2   47      NA      NA      NA
1972      119    2   48      NA      NA      NA
1973      119    2   49      NA      NA      NA
1974      119    2   50      NA      NA      NA
1975      119    2   51      NA      NA      NA
1976      119    2   52      NA      NA      NA
1977      120    1    1      NA      NA      NA
1978      120    1    2      NA      NA      NA
1979      120    1    3      NA      NA      NA
1980      120    1    4      NA      NA      NA
1981      120    1    5      NA      NA      NA
1982      120    1    6      NA      NA      NA
1983      120    1    7      NA      NA      NA
1984      120    1    8      NA      NA      NA
1985      120    1    9      NA      NA      NA
1986      120    1   10      NA      NA      NA
1987      120    1   11      NA      NA      NA
1988      120    1   12      NA      NA      NA
1989      120    1   13      NA      NA      NA
1990      120    1   14      NA      NA      NA
1991      120    1   15      NA      NA      NA
1992      120    1   16      NA      NA      NA
1993      120    1   17      NA      NA      NA
1994      120    1   18      NA      NA      NA
1995      120    1   19      NA      NA      NA
1996      120    1   20      NA      NA      NA
1997      120    1   21      NA      NA      NA
1998      120    1   22      NA      NA      NA
1999      120    1   23      NA      NA      NA
2000      120    1   24      NA      NA      NA
2001      120    1   25      NA      NA      NA
2002      120    1   26      NA      NA      NA
2003      120    1   27      NA      NA      NA
2004      120    1   28      NA      NA      NA
2005      120    1   29      NA      NA      NA
2006      120    1   30      NA      NA      NA
2007      120    1   31      NA      NA      NA
2008      120    1   32      NA      NA      NA
2009      120    1   33      NA      NA      NA
2010      120    1   34      NA      NA      NA
2011      120    1   35      NA      NA      NA
2012      120    1   36      NA      NA      NA
2013      120    1   37      NA      NA      NA
2014      120    1   38      NA      NA      NA
2015      120    1   39      NA      NA      NA
2016      120    1   40      NA      NA      NA
2017      120    1   41      NA      NA      NA
2018      120    1   42      NA      NA      NA
2019      120    1   43      NA      NA      NA
2020      120    1   44      NA      NA      NA
2021      120    1   45      NA      NA      NA
2022      120    1   46      NA      NA      NA
2023      120    1   47      NA      NA      NA
2024      120    1   48      NA      NA      NA
2025      120    1   49      NA      NA      NA
2026      120    1   50      NA      NA      NA
2027      120    1   51      NA      NA      NA
2028      120    1   52      NA      NA      NA
2029      120    2    1      NA      NA      NA
2030      120    2    2      NA      NA      NA
2031      120    2    3      NA      NA      NA
2032      120    2    4      NA      NA      NA
2033      120    2    5      NA      NA      NA
2034      120    2    6      NA      NA      NA
2035      120    2    7      NA      NA      NA
2036      120    2    8      NA      NA      NA
2037      120    2    9      NA      NA      NA
2038      120    2   10      NA      NA      NA
2039      120    2   11      NA      NA      NA
2040      120    2   12      NA      NA      NA
2041      120    2   13      NA      NA      NA
2042      120    2   14      NA      NA      NA
2043      120    2   15      NA      NA      NA
2044      120    2   16      NA      NA      NA
2045      120    2   17      NA      NA      NA
2046      120    2   18      NA      NA      NA
2047      120    2   19      NA      NA      NA
2048      120    2   20      NA      NA      NA
2049      120    2   21      NA      NA      NA
2050      120    2   22      NA      NA      NA
2051      120    2   23      NA      NA      NA
2052      120    2   24      NA      NA      NA
2053      120    2   25      NA      NA      NA
2054      120    2   26      NA      NA      NA
2055      120    2   27      NA      NA      NA
2056      120    2   28      NA      NA      NA
2057      120    2   29      NA      NA      NA
2058      120    2   30      NA      NA      NA
2059      120    2   31      NA      NA      NA
2060      120    2   32      NA      NA      NA
2061      120    2   33      NA      NA      NA
2062      120    2   34      NA      NA      NA
2063      120    2   35      NA      NA      NA
2064      120    2   36      NA      NA      NA
2065      120    2   37      NA      NA      NA
2066      120    2   38      NA      NA      NA
2067      120    2   39      NA      NA      NA
2068      120    2   40      NA      NA      NA
2069      120    2   41      NA      NA      NA
2070      120    2   42      NA      NA      NA
2071      120    2   43      NA      NA      NA
2072      120    2   44      NA      NA      NA
2073      120    2   45      NA      NA      NA
2074      120    2   46      NA      NA      NA
2075      120    2   47      NA      NA      NA
2076      120    2   48      NA      NA      NA
2077      120    2   49      NA      NA      NA
2078      120    2   50      NA      NA      NA
2079      120    2   51      NA      NA      NA
2080      120    2   52      NA      NA      NA
     p2price p1prom p2prom country
1961      NA     NA     NA      CN
1962      NA     NA     NA      CN
1963      NA     NA     NA      CN
1964      NA     NA     NA      CN
1965      NA     NA     NA      CN
1966      NA     NA     NA      CN
1967      NA     NA     NA      CN
1968      NA     NA     NA      CN
1969      NA     NA     NA      CN
1970      NA     NA     NA      CN
1971      NA     NA     NA      CN
1972      NA     NA     NA      CN
1973      NA     NA     NA      CN
1974      NA     NA     NA      CN
1975      NA     NA     NA      CN
1976      NA     NA     NA      CN
1977      NA     NA     NA      CN
1978      NA     NA     NA      CN
1979      NA     NA     NA      CN
1980      NA     NA     NA      CN
1981      NA     NA     NA      CN
1982      NA     NA     NA      CN
1983      NA     NA     NA      CN
1984      NA     NA     NA      CN
1985      NA     NA     NA      CN
1986      NA     NA     NA      CN
1987      NA     NA     NA      CN
1988      NA     NA     NA      CN
1989      NA     NA     NA      CN
1990      NA     NA     NA      CN
1991      NA     NA     NA      CN
1992      NA     NA     NA      CN
1993      NA     NA     NA      CN
1994      NA     NA     NA      CN
1995      NA     NA     NA      CN
1996      NA     NA     NA      CN
1997      NA     NA     NA      CN
1998      NA     NA     NA      CN
1999      NA     NA     NA      CN
2000      NA     NA     NA      CN
2001      NA     NA     NA      CN
2002      NA     NA     NA      CN
2003      NA     NA     NA      CN
2004      NA     NA     NA      CN
2005      NA     NA     NA      CN
2006      NA     NA     NA      CN
2007      NA     NA     NA      CN
2008      NA     NA     NA      CN
2009      NA     NA     NA      CN
2010      NA     NA     NA      CN
2011      NA     NA     NA      CN
2012      NA     NA     NA      CN
2013      NA     NA     NA      CN
2014      NA     NA     NA      CN
2015      NA     NA     NA      CN
2016      NA     NA     NA      CN
2017      NA     NA     NA      CN
2018      NA     NA     NA      CN
2019      NA     NA     NA      CN
2020      NA     NA     NA      CN
2021      NA     NA     NA      CN
2022      NA     NA     NA      CN
2023      NA     NA     NA      CN
2024      NA     NA     NA      CN
2025      NA     NA     NA      CN
2026      NA     NA     NA      CN
2027      NA     NA     NA      CN
2028      NA     NA     NA      CN
2029      NA     NA     NA      CN
2030      NA     NA     NA      CN
2031      NA     NA     NA      CN
2032      NA     NA     NA      CN
2033      NA     NA     NA      CN
2034      NA     NA     NA      CN
2035      NA     NA     NA      CN
2036      NA     NA     NA      CN
2037      NA     NA     NA      CN
2038      NA     NA     NA      CN
2039      NA     NA     NA      CN
2040      NA     NA     NA      CN
2041      NA     NA     NA      CN
2042      NA     NA     NA      CN
2043      NA     NA     NA      CN
2044      NA     NA     NA      CN
2045      NA     NA     NA      CN
2046      NA     NA     NA      CN
2047      NA     NA     NA      CN
2048      NA     NA     NA      CN
2049      NA     NA     NA      CN
2050      NA     NA     NA      CN
2051      NA     NA     NA      CN
2052      NA     NA     NA      CN
2053      NA     NA     NA      CN
2054      NA     NA     NA      CN
2055      NA     NA     NA      CN
2056      NA     NA     NA      CN
2057      NA     NA     NA      CN
2058      NA     NA     NA      CN
2059      NA     NA     NA      CN
2060      NA     NA     NA      CN
2061      NA     NA     NA      CN
2062      NA     NA     NA      CN
2063      NA     NA     NA      CN
2064      NA     NA     NA      CN
2065      NA     NA     NA      CN
2066      NA     NA     NA      CN
2067      NA     NA     NA      CN
2068      NA     NA     NA      CN
2069      NA     NA     NA      CN
2070      NA     NA     NA      CN
2071      NA     NA     NA      CN
2072      NA     NA     NA      CN
2073      NA     NA     NA      CN
2074      NA     NA     NA      CN
2075      NA     NA     NA      CN
2076      NA     NA     NA      CN
2077      NA     NA     NA      CN
2078      NA     NA     NA      CN
2079      NA     NA     NA      CN
2080      NA     NA     NA      CN

3.1.2 Store Data: Simulating Data Points

We complete store.df with random data for store-by-week observations of the sales, price, and promotional status of 2 products.

!Before simulating random data, it is important to set the random number generation seed to make the process replicable.

?set.seed
set.seed(98250)  # a favorite US postal code
#now draw the random data; each row is one week of one year for one store
# we set the status of whether each product was promoted (value 1) by drawing from the binomial distribution that counts the number of “heads” in a collection of coin tosses (where the coin can have any proportion of heads, not just 50 %).
# we set the status of whether each product was promoted (value 1) by drawing from the binomial distribution that counts the number of “heads” in a collection of coin tosses (where the coin can have any proportion of heads, not just 50 %).
n=nrow(store.df)
n
[1] 2080

To detail that process: we use the rbinom(n, size, p) (decoded as “random binomial”) function to draw from the binomial distribution. For every row of the store data, as noted by n=nrow(store.df), we draw from a distribution representing the number of heads in a single coin toss (size=1) with a coin that has probability p=0.1 for product 1 and p=0.15 for product 2. In other words, we arbitrarily assign a 10 % likelihood of promotion for product 1, and 15% likelihood for product 2 and then randomly determine which weeks have promotions.

store.df$p1prom <- rbinom(n=nrow(store.df), size=1, p=0.1)  # 10% promoted
store.df$p2prom <- rbinom(n=nrow(store.df), size=1, p=0.15) # 15% promoted
head(store.df)  # how does it look so far? (not shown)
  storeNum Year Week p1sales p2sales p1price
1      101    1    1      NA      NA      NA
2      101    1    2      NA      NA      NA
3      101    1    3      NA      NA      NA
4      101    1    4      NA      NA      NA
5      101    1    5      NA      NA      NA
6      101    1    6      NA      NA      NA
  p2price p1prom p2prom country
1      NA      0      0      US
2      NA      0      0      US
3      NA      1      0      US
4      NA      0      0      US
5      NA      0      1      US
6      NA      0      0      US
#Next we set a price for each product in each row of the data. We suppose that each product is sold at one of five distinct price points ranging from $2.19 to $3.19 over- all. We randomly draw a price for each week by defining a vector with the five price pointsandusingsample(x, size, replace)todrawfromitasmanytimes as we have rows of data (size=nrow(store.df)). The five prices are sampled many times, so we sample with replacement (replace=TRUE):
store.df$p1price <- sample(x=c(2.19, 2.29, 2.49, 2.79, 2.99), size=nrow(store.df), replace=TRUE)
store.df$p2price <- sample(x=c(2.29, 2.49, 2.59, 2.99, 3.19),
  size=nrow(store.df), replace=TRUE)
head(store.df)  # now how does it look?
  storeNum Year Week p1sales p2sales p1price
1      101    1    1      NA      NA    2.29
2      101    1    2      NA      NA    2.49
3      101    1    3      NA      NA    2.99
4      101    1    4      NA      NA    2.99
5      101    1    5      NA      NA    2.49
6      101    1    6      NA      NA    2.79
  p2price p1prom p2prom country
1    2.29      0      0      US
2    2.49      0      0      US
3    2.99      1      0      US
4    3.19      0      0      US
5    2.59      0      1      US
6    2.49      0      0      US
# Question: if price occurs at five discrete levels, does that make it a factor variable???? That depends on the analytic question, but in general probably not. We often perform math on price, such as subtracting cost in order to find gross margin, multiplying by units to find total sales, and so forth. Thus, even though it may have only a few unique values, price is a number, not a factor.
# Our last step is to simulate the sales figures for each week. We calculate sales as a function of the relative prices of the two products along with the promotional status of each.
# Item sales are in unit counts, so we use the Poisson distribution to generate count data: rpois(n, lambda), where n is the number of draws and lambda is the mean value of units per week. We draw a random Poisson count for each row (nrow(store.df), and set the mean sales (lambda) of Product 1 to be higher than that of Product 2:
# sales data, using poisson (counts) distribution, rpois()
?rpois
ot <- rpois(5, lambda = 10)
p1price <- sample(x=c(2.19, 2.29, 2.49, 2.79, 2.99), size=5, replace=TRUE)
unit_price <-20
units_sold <- 5
unit_s_price <- 15
sales <- unit_price*units_sold
sales
[1] 100
sales/unit_s_price # you would need to sell this many more units at the unit_s_price to match revenue in the default unit_price
[1] 6.666667
ot
[1] 12 15 10  7 13
p1price
[1] 2.99 2.49 2.19 2.99 2.79
tt<-ot*p1price
tt
[1] 35.88 37.35 21.90 20.93 36.27
tt/p1price
[1] 12 15 10  7 13
ot1 <- rpois(5, lambda = 15)
mean(ot)
[1] 11.4
mean(ot1)
[1] 16.8
xa <- c(2,3,4,5)
ya <- c(4,6,8,10)
xa
[1] 2 3 4 5
log(xa)
[1] 0.6931472 1.0986123 1.3862944 1.6094379
ya
[1]  4  6  8 10
log(ya)
[1] 1.386294 1.791759 2.079442 2.302585
# sales data, using poisson (counts) distribution, rpois()
tmp.sales1 <- rpois(nrow(store.df), lambda = 120)
tmp.sales2 <- rpois(nrow(store.df), lambda = 100)
#Now we scale those counts up or down according to the relative prices. Price effects often follow a logarithmic function rather than a linear function, so we use log(price) here:
# scale sales according to the ratio of log(price)
tmp.sales1 <- tmp.sales1 * log(store.df$p2price) / log(store.df$p1price)
tmp.sales2 <- tmp.sales2 * log(store.df$p1price) / print(log(store.df$p2price))
   [1] 0.8285518 0.9122827 1.0952734 1.1600209
   [5] 0.9516579 0.9122827 1.1600209 0.8285518
   [9] 0.8285518 1.0952734 1.0952734 1.1600209
  [13] 0.9516579 0.8285518 0.9516579 0.8285518
  [17] 0.9516579 1.0952734 0.9516579 1.0952734
  [21] 0.9122827 0.9516579 0.9122827 0.8285518
  [25] 0.9122827 0.9516579 0.9122827 0.9516579
  [29] 1.0952734 0.8285518 1.1600209 0.9516579
  [33] 1.0952734 0.9122827 0.8285518 1.1600209
  [37] 1.1600209 1.0952734 1.1600209 1.1600209
  [41] 0.8285518 0.8285518 0.8285518 0.8285518
  [45] 0.9516579 0.9516579 1.0952734 1.0952734
  [49] 0.8285518 0.8285518 0.9516579 1.0952734
  [53] 1.1600209 0.8285518 0.9516579 0.9122827
  [57] 0.9122827 1.0952734 1.0952734 0.8285518
  [61] 0.9122827 0.8285518 0.9516579 0.9122827
  [65] 0.9516579 1.1600209 1.1600209 1.0952734
  [69] 0.9516579 0.9122827 0.9122827 1.0952734
  [73] 0.8285518 1.0952734 1.0952734 1.0952734
  [77] 0.9516579 0.9122827 0.8285518 1.0952734
  [81] 1.1600209 1.0952734 0.9122827 0.8285518
  [85] 0.9122827 1.0952734 1.0952734 0.9516579
  [89] 1.0952734 1.0952734 0.9122827 0.9516579
  [93] 1.0952734 1.0952734 0.9122827 0.9122827
  [97] 1.1600209 1.0952734 0.9516579 1.0952734
 [101] 0.8285518 1.0952734 0.9516579 1.1600209
 [105] 0.8285518 0.9122827 1.0952734 1.1600209
 [109] 0.9516579 0.9516579 0.9122827 1.1600209
 [113] 1.0952734 0.9122827 0.9122827 0.9516579
 [117] 1.1600209 0.8285518 1.0952734 0.9122827
 [121] 0.8285518 0.9516579 0.9516579 0.9122827
 [125] 1.1600209 0.8285518 0.8285518 1.1600209
 [129] 0.9122827 1.1600209 0.9516579 0.9516579
 [133] 1.1600209 1.0952734 0.9122827 1.1600209
 [137] 0.8285518 0.9122827 0.9516579 1.1600209
 [141] 1.1600209 0.8285518 0.8285518 0.9516579
 [145] 0.9122827 1.1600209 1.1600209 0.9516579
 [149] 0.9122827 1.1600209 0.9122827 0.8285518
 [153] 0.9516579 0.9516579 1.0952734 1.1600209
 [157] 0.8285518 1.1600209 0.9516579 1.1600209
 [161] 0.9516579 1.1600209 1.1600209 1.0952734
 [165] 0.8285518 1.1600209 0.8285518 0.8285518
 [169] 0.8285518 0.9516579 0.9516579 1.0952734
 [173] 1.0952734 0.9516579 1.1600209 0.9122827
 [177] 0.9122827 1.0952734 0.9516579 1.0952734
 [181] 0.8285518 0.9122827 1.1600209 0.9122827
 [185] 0.9516579 0.9122827 0.9122827 0.9516579
 [189] 0.9516579 0.8285518 1.1600209 1.1600209
 [193] 1.1600209 1.0952734 0.8285518 0.8285518
 [197] 1.1600209 0.8285518 0.9122827 1.1600209
 [201] 1.1600209 1.0952734 0.8285518 1.1600209
 [205] 0.9122827 1.0952734 0.8285518 1.0952734
 [209] 1.0952734 1.0952734 1.0952734 0.9122827
 [213] 0.9516579 0.9516579 1.0952734 0.9122827
 [217] 0.8285518 0.8285518 0.9516579 0.8285518
 [221] 0.8285518 0.9516579 0.9122827 0.9122827
 [225] 1.0952734 0.9516579 0.9516579 0.9122827
 [229] 0.9122827 0.9516579 0.8285518 0.9122827
 [233] 1.0952734 1.0952734 1.1600209 0.9516579
 [237] 1.1600209 0.9122827 0.8285518 0.8285518
 [241] 1.0952734 0.8285518 0.9516579 0.9516579
 [245] 1.1600209 0.9516579 1.1600209 0.9122827
 [249] 0.8285518 0.8285518 0.8285518 0.9516579
 [253] 0.9516579 1.0952734 1.1600209 1.1600209
 [257] 0.8285518 0.9122827 1.1600209 0.9122827
 [261] 0.9122827 1.0952734 0.9122827 1.0952734
 [265] 0.9122827 0.9516579 0.9516579 0.9516579
 [269] 0.9122827 0.8285518 1.1600209 0.8285518
 [273] 1.0952734 1.0952734 1.1600209 1.1600209
 [277] 1.1600209 0.9516579 0.9122827 0.8285518
 [281] 1.1600209 0.8285518 1.1600209 1.0952734
 [285] 1.1600209 0.8285518 0.9516579 0.9516579
 [289] 1.0952734 1.1600209 1.0952734 0.9122827
 [293] 1.1600209 1.0952734 0.8285518 0.9516579
 [297] 0.9516579 1.1600209 0.8285518 0.9516579
 [301] 0.9516579 1.1600209 0.8285518 1.1600209
 [305] 0.9122827 1.1600209 1.1600209 1.1600209
 [309] 1.1600209 0.9516579 0.9516579 1.0952734
 [313] 1.0952734 1.1600209 1.1600209 0.9122827
 [317] 0.8285518 0.9516579 1.1600209 0.9122827
 [321] 0.8285518 0.9122827 0.9516579 0.8285518
 [325] 0.8285518 0.9516579 1.1600209 1.0952734
 [329] 0.9122827 0.9516579 0.8285518 1.0952734
 [333] 0.9516579 0.8285518 1.0952734 0.9516579
 [337] 0.9516579 0.8285518 1.0952734 1.0952734
 [341] 1.1600209 0.9122827 0.9122827 0.8285518
 [345] 1.1600209 0.8285518 0.8285518 0.9516579
 [349] 1.0952734 0.9122827 0.9122827 0.9516579
 [353] 1.0952734 1.1600209 1.1600209 1.0952734
 [357] 0.9516579 0.8285518 1.1600209 0.9122827
 [361] 1.0952734 1.1600209 0.9516579 1.0952734
 [365] 1.1600209 1.1600209 0.9516579 1.0952734
 [369] 1.1600209 0.8285518 0.9122827 0.8285518
 [373] 1.1600209 0.9122827 1.1600209 0.8285518
 [377] 0.9516579 0.8285518 1.1600209 0.8285518
 [381] 1.0952734 0.9516579 0.9122827 1.1600209
 [385] 0.9516579 1.1600209 0.9516579 0.9122827
 [389] 1.0952734 0.8285518 0.9516579 0.9516579
 [393] 0.9122827 0.9122827 1.1600209 0.8285518
 [397] 1.1600209 0.9122827 1.1600209 0.9516579
 [401] 0.9516579 0.8285518 1.1600209 1.1600209
 [405] 0.8285518 1.0952734 0.9516579 0.9516579
 [409] 0.9516579 1.1600209 1.0952734 1.1600209
 [413] 0.8285518 1.0952734 1.1600209 0.8285518
 [417] 0.8285518 1.1600209 1.0952734 0.9122827
 [421] 0.8285518 1.0952734 0.9516579 0.9516579
 [425] 0.9516579 0.9516579 0.8285518 0.9516579
 [429] 0.9122827 1.1600209 0.9122827 0.8285518
 [433] 1.1600209 0.9122827 1.0952734 0.9122827
 [437] 1.0952734 0.9122827 0.9516579 0.9516579
 [441] 0.9516579 0.9122827 0.9122827 0.9516579
 [445] 0.9516579 0.9122827 1.1600209 0.8285518
 [449] 0.9122827 0.8285518 1.0952734 1.1600209
 [453] 1.1600209 0.8285518 0.8285518 1.1600209
 [457] 0.8285518 0.8285518 0.9516579 0.8285518
 [461] 0.8285518 1.0952734 0.9516579 1.1600209
 [465] 0.8285518 1.0952734 0.8285518 0.9516579
 [469] 1.0952734 0.9122827 0.9122827 1.1600209
 [473] 1.1600209 0.9122827 0.9122827 0.9516579
 [477] 0.9516579 0.8285518 1.0952734 1.1600209
 [481] 1.0952734 0.9122827 1.1600209 0.9516579
 [485] 0.8285518 0.8285518 1.0952734 0.8285518
 [489] 0.9516579 0.9516579 0.9516579 0.8285518
 [493] 0.8285518 1.1600209 0.9516579 1.0952734
 [497] 1.0952734 0.9516579 0.8285518 1.0952734
 [501] 1.0952734 0.9122827 0.9516579 0.9516579
 [505] 0.9122827 0.9122827 0.8285518 0.9516579
 [509] 0.8285518 1.1600209 0.9122827 1.0952734
 [513] 1.1600209 0.8285518 0.9516579 0.9122827
 [517] 1.0952734 1.0952734 0.9122827 1.1600209
 [521] 0.9122827 0.8285518 0.8285518 0.8285518
 [525] 1.0952734 1.1600209 0.9516579 0.8285518
 [529] 0.8285518 0.8285518 0.8285518 1.1600209
 [533] 0.8285518 0.9122827 1.1600209 0.9516579
 [537] 0.9122827 0.9516579 0.8285518 0.9516579
 [541] 1.0952734 1.1600209 0.8285518 1.0952734
 [545] 1.0952734 1.0952734 0.9516579 1.1600209
 [549] 0.8285518 0.9516579 1.1600209 0.9516579
 [553] 1.1600209 0.9516579 1.1600209 0.8285518
 [557] 1.0952734 0.8285518 0.9122827 0.9122827
 [561] 1.1600209 0.9516579 0.9516579 1.0952734
 [565] 0.9516579 0.8285518 1.1600209 0.9122827
 [569] 0.9516579 0.9516579 0.9516579 0.9122827
 [573] 1.1600209 0.9122827 1.1600209 0.9122827
 [577] 0.9122827 1.0952734 1.1600209 0.9516579
 [581] 0.8285518 0.8285518 0.8285518 0.9516579
 [585] 0.8285518 0.9122827 0.9516579 1.0952734
 [589] 1.0952734 1.1600209 1.1600209 1.1600209
 [593] 1.1600209 0.9516579 0.9516579 0.9516579
 [597] 1.1600209 0.9122827 0.9516579 1.0952734
 [601] 1.1600209 0.9516579 1.1600209 0.8285518
 [605] 1.1600209 0.9516579 1.1600209 0.9516579
 [609] 0.8285518 0.9516579 0.9516579 1.0952734
 [613] 0.8285518 1.1600209 0.9516579 0.9122827
 [617] 0.9122827 0.9516579 0.9516579 1.0952734
 [621] 1.0952734 0.8285518 1.1600209 1.1600209
 [625] 0.8285518 0.9122827 1.1600209 0.9122827
 [629] 0.9516579 1.0952734 0.8285518 0.9516579
 [633] 0.9122827 1.0952734 0.9122827 0.9122827
 [637] 0.9122827 0.8285518 0.8285518 0.8285518
 [641] 1.1600209 0.8285518 0.9122827 0.9516579
 [645] 1.0952734 1.1600209 0.9122827 1.1600209
 [649] 0.9122827 0.8285518 0.9516579 1.1600209
 [653] 0.9516579 0.9516579 0.9516579 0.8285518
 [657] 0.9516579 1.0952734 0.9516579 0.8285518
 [661] 0.9516579 1.0952734 0.9516579 1.1600209
 [665] 0.8285518 0.9516579 0.9516579 0.9516579
 [669] 0.9516579 0.8285518 1.0952734 0.8285518
 [673] 1.0952734 1.0952734 0.9516579 0.9122827
 [677] 0.9516579 0.9122827 1.1600209 0.9516579
 [681] 0.8285518 0.8285518 1.1600209 1.1600209
 [685] 0.8285518 0.9122827 1.0952734 1.1600209
 [689] 1.0952734 0.8285518 0.9122827 0.9122827
 [693] 0.9516579 0.9122827 1.0952734 0.9122827
 [697] 1.0952734 1.0952734 1.0952734 0.9122827
 [701] 0.9122827 0.8285518 1.1600209 1.0952734
 [705] 1.0952734 0.9516579 1.0952734 1.0952734
 [709] 1.0952734 0.8285518 0.9122827 1.0952734
 [713] 0.8285518 0.9122827 0.9516579 1.0952734
 [717] 0.8285518 0.8285518 0.8285518 1.1600209
 [721] 1.1600209 0.8285518 1.1600209 1.0952734
 [725] 1.0952734 1.1600209 1.0952734 1.1600209
 [729] 0.9516579 0.9122827 1.1600209 1.0952734
 [733] 0.9122827 0.9122827 1.1600209 1.1600209
 [737] 0.9516579 0.9122827 1.0952734 0.9122827
 [741] 0.8285518 0.9122827 0.8285518 0.9516579
 [745] 0.9516579 1.0952734 0.9516579 0.9122827
 [749] 0.9122827 0.9516579 0.9516579 1.0952734
 [753] 1.1600209 1.0952734 0.9122827 0.9122827
 [757] 0.9122827 0.9516579 0.9122827 1.1600209
 [761] 0.9516579 1.1600209 0.8285518 1.0952734
 [765] 1.0952734 0.9122827 0.9516579 0.9516579
 [769] 1.1600209 0.9122827 0.9122827 0.8285518
 [773] 0.9516579 1.1600209 0.8285518 0.9516579
 [777] 0.9516579 0.9516579 1.1600209 1.0952734
 [781] 1.0952734 1.1600209 0.8285518 0.9122827
 [785] 0.9516579 0.8285518 0.9516579 1.1600209
 [789] 0.8285518 1.1600209 1.1600209 0.8285518
 [793] 0.9122827 0.9516579 0.9122827 1.1600209
 [797] 0.8285518 1.1600209 0.8285518 0.9122827
 [801] 0.9516579 0.9516579 0.8285518 1.0952734
 [805] 1.1600209 0.9122827 1.0952734 0.9516579
 [809] 0.9516579 1.1600209 1.0952734 0.9122827
 [813] 0.9122827 1.0952734 0.9122827 1.0952734
 [817] 0.8285518 0.9516579 1.0952734 1.1600209
 [821] 0.8285518 0.9516579 0.8285518 1.0952734
 [825] 0.9122827 1.1600209 0.9516579 0.9516579
 [829] 0.8285518 0.9122827 0.9516579 0.9516579
 [833] 0.9122827 1.0952734 0.9516579 0.9122827
 [837] 0.9516579 0.9122827 1.1600209 0.9516579
 [841] 0.9122827 1.1600209 1.1600209 0.8285518
 [845] 0.9516579 1.0952734 0.9516579 1.1600209
 [849] 1.0952734 1.0952734 0.8285518 0.9516579
 [853] 0.8285518 0.9516579 1.1600209 0.9122827
 [857] 0.8285518 0.9122827 1.0952734 0.9122827
 [861] 0.9516579 0.9516579 0.9122827 0.9516579
 [865] 1.0952734 1.0952734 0.9516579 1.1600209
 [869] 0.9516579 1.0952734 1.1600209 0.9516579
 [873] 1.0952734 1.1600209 0.8285518 0.9122827
 [877] 0.9516579 0.9516579 0.9516579 1.0952734
 [881] 0.8285518 1.1600209 0.8285518 1.0952734
 [885] 1.0952734 1.1600209 1.0952734 0.9122827
 [889] 0.8285518 0.9516579 0.9516579 0.9516579
 [893] 0.9516579 1.1600209 0.8285518 0.9122827
 [897] 1.0952734 1.1600209 1.1600209 1.0952734
 [901] 0.9122827 0.9122827 0.8285518 1.1600209
 [905] 0.8285518 0.8285518 0.8285518 1.1600209
 [909] 1.1600209 0.8285518 0.9122827 0.8285518
 [913] 0.9122827 0.9122827 0.9122827 0.8285518
 [917] 1.1600209 0.9516579 0.9516579 0.9122827
 [921] 1.0952734 0.9516579 0.8285518 0.9122827
 [925] 1.1600209 1.1600209 1.1600209 1.1600209
 [929] 0.9516579 0.8285518 0.8285518 1.1600209
 [933] 0.9516579 1.0952734 1.0952734 0.9122827
 [937] 0.9122827 0.8285518 0.9516579 0.9516579
 [941] 1.0952734 0.8285518 1.1600209 1.1600209
 [945] 1.0952734 0.8285518 0.9516579 0.9516579
 [949] 0.9122827 1.0952734 1.1600209 1.1600209
 [953] 1.0952734 0.9516579 1.1600209 1.0952734
 [957] 1.0952734 0.9516579 1.0952734 0.8285518
 [961] 1.1600209 0.8285518 0.8285518 1.1600209
 [965] 0.9516579 0.9122827 1.0952734 0.8285518
 [969] 0.9122827 0.9122827 0.9122827 1.1600209
 [973] 1.0952734 1.1600209 0.9516579 1.1600209
 [977] 1.0952734 0.9122827 1.1600209 0.9516579
 [981] 1.0952734 1.1600209 0.8285518 0.8285518
 [985] 0.9122827 0.9516579 0.8285518 0.9516579
 [989] 1.1600209 0.9122827 0.9122827 1.1600209
 [993] 0.9122827 0.9516579 0.8285518 1.0952734
 [997] 0.9122827 1.1600209 0.8285518 1.0952734
[1001] 0.9516579 0.9122827 1.1600209 0.9516579
[1005] 0.8285518 1.1600209 0.9516579 0.8285518
[1009] 0.9122827 0.9122827 1.1600209 0.9516579
[1013] 1.0952734 1.0952734 0.9516579 1.0952734
[1017] 0.9516579 0.8285518 1.0952734 1.1600209
[1021] 0.9516579 0.9122827 1.0952734 0.9516579
[1025] 0.9122827 0.9122827 1.0952734 1.1600209
[1029] 0.8285518 1.0952734 0.9122827 1.1600209
[1033] 0.9122827 0.9122827 0.9516579 0.8285518
[1037] 1.0952734 0.9516579 0.9516579 0.9516579
[1041] 0.9122827 0.9516579 0.9516579 1.1600209
[1045] 0.9122827 0.8285518 0.9122827 1.1600209
[1049] 1.0952734 0.9516579 0.9122827 0.9516579
[1053] 0.8285518 1.1600209 0.9516579 0.9122827
[1057] 1.1600209 0.9122827 0.8285518 1.0952734
[1061] 0.9516579 1.0952734 0.9122827 0.8285518
[1065] 0.9122827 0.8285518 1.1600209 1.0952734
[1069] 0.8285518 0.8285518 0.9516579 0.8285518
[1073] 1.0952734 0.9516579 0.9516579 1.1600209
[1077] 0.9122827 1.1600209 1.0952734 1.0952734
[1081] 1.1600209 1.1600209 0.9516579 1.1600209
[1085] 0.8285518 0.9516579 0.8285518 1.0952734
[1089] 1.1600209 0.9122827 0.9516579 0.8285518
[1093] 0.9516579 0.9122827 0.9122827 0.9516579
[1097] 1.0952734 0.9122827 0.9122827 0.8285518
[1101] 0.9122827 0.8285518 0.9122827 0.9122827
[1105] 0.9516579 0.9516579 1.0952734 0.8285518
[1109] 1.1600209 0.8285518 0.9516579 0.8285518
[1113] 0.9122827 1.1600209 1.0952734 0.8285518
[1117] 0.9516579 0.9516579 1.0952734 0.9516579
[1121] 1.0952734 1.0952734 0.9516579 0.9122827
[1125] 0.8285518 0.8285518 1.1600209 1.0952734
[1129] 1.0952734 1.1600209 1.1600209 0.8285518
[1133] 0.9516579 0.8285518 0.8285518 0.9516579
[1137] 0.9122827 1.0952734 0.8285518 0.9516579
[1141] 1.1600209 0.8285518 0.9122827 1.0952734
[1145] 0.9516579 0.9122827 0.9122827 0.9122827
[1149] 1.0952734 0.9122827 1.0952734 0.9122827
[1153] 0.9122827 1.1600209 1.0952734 0.8285518
[1157] 0.8285518 0.9122827 0.8285518 1.0952734
[1161] 1.0952734 0.8285518 0.9122827 1.0952734
[1165] 1.1600209 1.0952734 1.0952734 0.9122827
[1169] 0.8285518 1.1600209 0.8285518 1.1600209
[1173] 0.9516579 1.0952734 0.9122827 0.9122827
[1177] 0.9516579 0.9122827 0.9516579 0.8285518
[1181] 1.1600209 1.0952734 0.9516579 0.8285518
[1185] 1.1600209 0.8285518 1.1600209 0.8285518
[1189] 1.0952734 0.9122827 0.8285518 0.9516579
[1193] 0.9122827 0.8285518 0.9516579 1.1600209
[1197] 0.9122827 1.0952734 0.8285518 0.8285518
[1201] 0.9516579 1.1600209 1.1600209 1.1600209
[1205] 1.0952734 0.8285518 0.8285518 1.0952734
[1209] 1.1600209 0.8285518 0.8285518 0.8285518
[1213] 1.1600209 1.0952734 1.1600209 0.9516579
[1217] 0.8285518 0.9516579 1.1600209 0.9122827
[1221] 0.9122827 0.9516579 0.8285518 0.9516579
[1225] 0.9122827 0.9516579 0.9122827 0.9516579
[1229] 0.9122827 0.8285518 1.1600209 1.1600209
[1233] 1.1600209 0.9516579 0.9122827 0.9122827
[1237] 0.9516579 1.0952734 0.8285518 0.9122827
[1241] 0.9122827 1.1600209 0.9516579 0.9122827
[1245] 0.8285518 0.9122827 1.1600209 1.0952734
[1249] 0.8285518 1.0952734 1.1600209 1.1600209
[1253] 1.1600209 1.0952734 1.0952734 0.9122827
[1257] 1.0952734 0.9122827 0.9122827 1.0952734
[1261] 0.8285518 0.9516579 1.1600209 1.1600209
[1265] 0.9122827 0.9516579 0.9516579 0.9516579
[1269] 1.0952734 1.1600209 1.1600209 0.9122827
[1273] 1.1600209 0.8285518 1.1600209 0.9516579
[1277] 0.9122827 0.8285518 1.0952734 0.8285518
[1281] 1.1600209 0.8285518 1.0952734 0.8285518
[1285] 1.1600209 0.9516579 0.8285518 0.9516579
[1289] 1.0952734 0.9122827 0.9122827 0.8285518
[1293] 1.1600209 1.0952734 0.8285518 0.9516579
[1297] 1.0952734 1.1600209 1.1600209 1.1600209
[1301] 0.8285518 0.8285518 1.1600209 0.9516579
[1305] 0.9122827 0.9516579 0.8285518 0.9122827
[1309] 1.0952734 1.1600209 0.9516579 0.8285518
[1313] 0.9122827 0.9516579 1.0952734 0.9122827
[1317] 0.9516579 0.9516579 0.9122827 1.1600209
[1321] 0.9516579 1.1600209 0.9122827 0.9122827
[1325] 1.1600209 0.9122827 0.9516579 0.9122827
[1329] 0.9122827 0.8285518 1.1600209 1.1600209
[1333] 0.8285518 0.9122827 0.9516579 1.1600209
[1337] 0.9122827 0.8285518 0.8285518 0.8285518
[1341] 0.9516579 1.1600209 0.8285518 1.0952734
[1345] 1.1600209 0.8285518 0.8285518 1.1600209
[1349] 0.9516579 0.9516579 0.9122827 0.8285518
[1353] 0.8285518 0.9516579 0.8285518 1.1600209
[1357] 1.0952734 0.9516579 0.9122827 1.1600209
[1361] 0.9122827 1.1600209 0.9516579 0.9122827
[1365] 0.8285518 0.8285518 1.1600209 1.1600209
[1369] 1.0952734 0.9122827 0.8285518 1.0952734
[1373] 0.9516579 1.1600209 1.0952734 1.1600209
[1377] 1.1600209 0.8285518 0.8285518 1.0952734
[1381] 0.8285518 1.1600209 1.0952734 0.9122827
[1385] 1.1600209 0.8285518 0.8285518 0.9516579
[1389] 1.1600209 0.8285518 0.9122827 0.9122827
[1393] 1.1600209 0.9122827 0.9516579 0.9122827
[1397] 0.8285518 0.9122827 1.1600209 0.9516579
[1401] 0.9516579 0.9516579 0.9516579 0.9122827
[1405] 1.1600209 0.9516579 0.9516579 0.8285518
[1409] 1.0952734 1.1600209 0.9122827 1.1600209
[1413] 1.0952734 0.9516579 0.9122827 0.9122827
[1417] 1.1600209 1.0952734 0.9516579 1.1600209
[1421] 0.9516579 0.9516579 1.1600209 0.9516579
[1425] 0.9122827 0.9122827 0.9516579 1.0952734
[1429] 1.0952734 0.8285518 0.9516579 1.0952734
[1433] 0.8285518 0.8285518 0.9516579 1.0952734
[1437] 1.1600209 0.8285518 0.8285518 0.9516579
[1441] 0.8285518 1.0952734 1.0952734 0.9516579
[1445] 1.0952734 1.0952734 0.8285518 0.9122827
[1449] 0.9122827 0.9516579 0.9122827 0.8285518
[1453] 1.1600209 0.9516579 1.0952734 1.1600209
[1457] 0.8285518 0.8285518 0.8285518 0.9122827
[1461] 0.9516579 1.1600209 1.1600209 0.9122827
[1465] 0.9516579 0.9516579 1.0952734 0.8285518
[1469] 0.9122827 0.8285518 1.0952734 0.8285518
[1473] 1.1600209 1.1600209 0.9516579 1.0952734
[1477] 1.1600209 1.1600209 0.9516579 0.8285518
[1481] 0.9122827 1.1600209 1.0952734 0.9516579
[1485] 0.9516579 0.8285518 1.1600209 1.0952734
[1489] 1.0952734 1.1600209 0.9516579 1.0952734
[1493] 0.9516579 0.9516579 1.1600209 0.8285518
[1497] 0.8285518 0.8285518 1.1600209 0.8285518
[1501] 0.8285518 1.1600209 0.8285518 1.0952734
[1505] 0.9122827 0.9122827 0.9122827 0.9516579
[1509] 0.9122827 0.9516579 0.9122827 0.9122827
[1513] 1.1600209 0.9122827 1.0952734 0.8285518
[1517] 1.1600209 1.1600209 0.8285518 0.8285518
[1521] 0.9516579 1.0952734 1.1600209 0.9516579
[1525] 1.1600209 0.8285518 0.8285518 0.9516579
[1529] 0.9516579 0.8285518 0.9122827 0.9122827
[1533] 0.8285518 1.0952734 0.9122827 1.0952734
[1537] 1.1600209 0.9516579 0.8285518 0.9516579
[1541] 0.8285518 1.0952734 0.8285518 0.9516579
[1545] 0.9516579 0.8285518 0.9516579 0.8285518
[1549] 0.8285518 1.0952734 0.9122827 1.1600209
[1553] 1.0952734 0.9516579 0.9122827 0.8285518
[1557] 0.9122827 0.8285518 0.9516579 1.0952734
[1561] 1.0952734 0.8285518 0.9516579 0.9122827
[1565] 1.1600209 0.9516579 0.9516579 1.1600209
[1569] 1.0952734 0.8285518 0.9516579 0.9122827
[1573] 0.9122827 1.1600209 0.9122827 1.1600209
[1577] 0.9122827 0.8285518 0.8285518 0.9122827
[1581] 0.8285518 1.1600209 0.8285518 1.1600209
[1585] 0.8285518 0.9516579 1.0952734 1.1600209
[1589] 1.0952734 0.9516579 1.0952734 0.9122827
[1593] 1.0952734 1.0952734 1.0952734 1.1600209
[1597] 0.9122827 1.1600209 0.8285518 1.0952734
[1601] 0.9516579 0.9516579 0.9516579 0.8285518
[1605] 1.1600209 1.0952734 1.0952734 0.9516579
[1609] 0.9516579 0.8285518 1.1600209 1.1600209
[1613] 0.8285518 1.1600209 0.9516579 1.1600209
[1617] 0.8285518 1.0952734 0.8285518 0.8285518
[1621] 1.0952734 0.9516579 0.9122827 1.0952734
[1625] 1.0952734 0.9516579 0.9516579 0.9122827
[1629] 1.1600209 0.9122827 0.9516579 0.8285518
[1633] 1.0952734 1.1600209 0.8285518 0.8285518
[1637] 0.8285518 0.9122827 0.9122827 0.8285518
[1641] 0.9122827 0.8285518 1.1600209 1.0952734
[1645] 0.9516579 1.1600209 0.9122827 1.0952734
[1649] 0.9122827 1.0952734 1.1600209 1.0952734
[1653] 0.9122827 1.0952734 1.0952734 0.8285518
[1657] 1.1600209 1.0952734 0.9516579 0.9516579
[1661] 0.8285518 1.1600209 0.8285518 0.8285518
[1665] 0.9122827 0.9122827 0.9516579 1.1600209
[1669] 0.9122827 0.9516579 0.8285518 0.8285518
[1673] 0.8285518 0.8285518 0.8285518 0.8285518
[1677] 0.8285518 1.1600209 1.0952734 0.9122827
[1681] 0.9516579 0.9122827 0.9516579 0.9122827
[1685] 0.9122827 0.9516579 1.1600209 0.9122827
[1689] 1.0952734 1.0952734 0.9516579 1.1600209
[1693] 0.8285518 1.1600209 1.0952734 1.1600209
[1697] 0.9122827 0.9122827 1.1600209 0.9122827
[1701] 0.9122827 0.8285518 0.9122827 0.9516579
[1705] 0.9516579 0.9122827 1.1600209 0.8285518
[1709] 1.1600209 0.9122827 1.0952734 1.1600209
[1713] 0.9122827 1.0952734 1.0952734 1.0952734
[1717] 1.1600209 1.1600209 1.1600209 1.0952734
[1721] 0.9122827 0.8285518 0.8285518 0.8285518
[1725] 1.0952734 0.8285518 0.9122827 0.9516579
[1729] 1.0952734 0.9516579 0.8285518 1.0952734
[1733] 0.9516579 0.8285518 1.0952734 0.9516579
[1737] 0.9122827 0.9122827 1.0952734 0.9516579
[1741] 1.1600209 1.1600209 1.0952734 0.9122827
[1745] 0.9122827 0.9516579 1.1600209 0.9516579
[1749] 0.9516579 1.0952734 0.8285518 0.9122827
[1753] 0.9122827 0.8285518 0.8285518 1.0952734
[1757] 0.9516579 0.9516579 0.9122827 1.0952734
[1761] 0.9516579 1.0952734 1.1600209 0.9122827
[1765] 0.9516579 0.8285518 1.1600209 0.9122827
[1769] 0.9516579 1.0952734 1.0952734 0.8285518
[1773] 0.9516579 0.9516579 0.8285518 1.0952734
[1777] 0.9516579 0.8285518 0.8285518 0.9516579
[1781] 1.1600209 1.1600209 0.9516579 1.1600209
[1785] 1.1600209 0.9122827 1.0952734 1.0952734
[1789] 0.8285518 1.0952734 0.8285518 0.8285518
[1793] 1.1600209 1.1600209 0.9516579 0.9516579
[1797] 1.0952734 0.9122827 0.9122827 0.9122827
[1801] 1.0952734 1.0952734 1.0952734 0.9516579
[1805] 1.0952734 1.0952734 0.8285518 0.9122827
[1809] 0.9122827 0.9122827 1.0952734 0.9122827
[1813] 1.1600209 0.8285518 0.9122827 0.9516579
[1817] 0.8285518 0.9122827 1.0952734 1.0952734
[1821] 1.1600209 0.9516579 1.0952734 0.9122827
[1825] 1.1600209 0.9122827 0.9516579 1.0952734
[1829] 0.8285518 0.9516579 0.9516579 0.9122827
[1833] 1.0952734 0.9516579 0.9122827 1.0952734
[1837] 1.1600209 0.9122827 1.0952734 0.9516579
[1841] 1.1600209 1.0952734 1.1600209 1.0952734
[1845] 1.1600209 0.9516579 0.9516579 1.1600209
[1849] 1.0952734 1.1600209 1.0952734 1.0952734
[1853] 1.0952734 1.1600209 0.8285518 1.1600209
[1857] 0.9122827 0.8285518 1.0952734 0.8285518
[1861] 0.9516579 1.0952734 0.8285518 0.8285518
[1865] 0.9516579 0.8285518 0.9516579 1.0952734
[1869] 1.1600209 1.0952734 0.9122827 0.9122827
[1873] 0.9516579 0.9122827 0.9516579 0.9122827
[1877] 1.0952734 1.0952734 0.9122827 1.0952734
[1881] 0.8285518 0.9516579 0.8285518 0.8285518
[1885] 0.9516579 0.9516579 1.0952734 0.9516579
[1889] 1.0952734 0.8285518 0.8285518 0.8285518
[1893] 0.8285518 1.1600209 1.1600209 1.1600209
[1897] 1.0952734 0.9122827 1.1600209 1.1600209
[1901] 1.0952734 1.1600209 1.0952734 0.8285518
[1905] 0.8285518 0.9122827 0.9122827 1.1600209
[1909] 1.1600209 0.9122827 0.9516579 1.1600209
[1913] 0.8285518 0.8285518 1.1600209 0.9122827
[1917] 0.8285518 0.8285518 0.8285518 0.8285518
[1921] 0.9122827 0.9516579 0.9122827 1.0952734
[1925] 1.1600209 0.9122827 0.9516579 1.0952734
[1929] 0.9516579 0.9516579 0.9122827 1.1600209
[1933] 0.9122827 0.9122827 1.1600209 1.1600209
[1937] 0.8285518 0.8285518 0.9122827 1.1600209
[1941] 1.0952734 1.0952734 1.0952734 0.8285518
[1945] 0.8285518 1.1600209 1.0952734 0.9516579
[1949] 1.0952734 0.8285518 0.9122827 1.0952734
[1953] 0.8285518 1.0952734 0.9122827 1.0952734
[1957] 0.8285518 1.1600209 0.9516579 0.9516579
[1961] 1.0952734 1.0952734 0.9122827 0.9516579
[1965] 0.8285518 0.9122827 1.0952734 0.9516579
[1969] 1.0952734 0.9122827 0.9516579 1.1600209
[1973] 1.0952734 1.1600209 0.8285518 0.8285518
[1977] 0.8285518 1.0952734 0.8285518 0.9122827
[1981] 1.0952734 0.9516579 0.8285518 1.1600209
[1985] 0.9122827 0.9516579 1.1600209 0.9516579
[1989] 0.8285518 0.9516579 1.1600209 0.9122827
[1993] 1.1600209 0.9122827 0.9516579 1.0952734
[1997] 0.8285518 0.9516579 0.8285518 0.8285518
[2001] 1.0952734 0.9516579 0.8285518 1.0952734
[2005] 0.9516579 0.9122827 0.9516579 0.8285518
[2009] 0.9516579 0.9122827 0.8285518 1.0952734
[2013] 0.9516579 0.9516579 1.0952734 0.9122827
[2017] 1.1600209 1.1600209 1.0952734 1.0952734
[2021] 1.0952734 0.9516579 0.9516579 0.8285518
[2025] 1.1600209 1.1600209 1.0952734 0.9516579
[2029] 0.9122827 0.9122827 0.9516579 1.0952734
[2033] 0.9122827 0.9122827 1.1600209 1.0952734
[2037] 0.9122827 0.8285518 1.0952734 0.9122827
[2041] 0.9122827 1.0952734 0.9516579 1.1600209
[2045] 0.9122827 1.1600209 0.9516579 1.1600209
[2049] 0.9122827 0.9516579 0.8285518 0.9122827
[2053] 0.9516579 0.8285518 0.9122827 0.8285518
[2057] 0.9516579 0.9516579 1.0952734 1.1600209
[2061] 0.9122827 0.9122827 0.9122827 0.9516579
[2065] 0.9122827 0.9122827 0.9122827 0.9516579
[2069] 1.1600209 1.1600209 0.9516579 1.1600209
[2073] 0.9122827 0.9122827 0.9516579 1.0952734
[2077] 0.9516579 0.8285518 0.8285518 0.9122827
# final sales get a 30% or 40% lift when promoted
store.df$p1sales <- floor(tmp.sales1 * (1 + store.df$p1prom*0.3))
store.df$p2sales <- floor(tmp.sales2 * (1 + store.df$p2prom*0.4))
# head(store.df)
library(car)

Attaching package: ‘car’

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

    some
# gives us a random set to inspect
some(store.df, 10)
     storeNum Year Week p1sales p2sales p1price
481       105    2   13     136     140    2.79
594       106    2   22      98     101    2.79
701       107    2   25     152      95    2.19
765       108    1   37     114     100    2.79
773       108    1   45     135      98    2.19
875       109    1   43     112     104    2.49
1070      111    1   30     107     102    2.49
1175      112    1   31      94     126    2.99
1735      117    2   19     129     104    2.99
1814      118    1   46     134      93    2.29
     p2price p1prom p2prom country
481     2.99      0      1      DE
594     2.59      0      0      DE
701     2.49      0      0      DE
765     2.99      0      1      DE
773     2.59      0      0      DE
875     2.29      0      0      GB
1070    2.29      0      0      GB
1175    2.49      0      0      BR
1735    2.99      0      0      JP
1814    2.29      0      0      AU

3.2 Functions to Summarise a Variable

Observations may comprise either discrete or continuous data with many possible values.

\[s^{2} = \frac{\sum(x - \bar{x})^2}{n - 1} \]

hist(store.df$p1price)
p1.table <- table(store.df$p1price)
p1.table

2.19 2.29 2.49 2.79 2.99 
 395  444  423  443  375 
plot(p1.table)

object.size(store.df)
120432 bytes
object.size(p1.table)
1136 bytes

  • Above #You can see from the resulting bar plot in Fig. Above that the product was on sale at each price point roughly the same number of times. R chose a type of plot suitable for our table object, but it is fairly ugly and the labels could be clearer. Later in this chapter we show how to modify a plot to get better results.

An analyst might want to know how often each product was promoted at each price point. The table() command produces two-way cross tabs when a second variable is included:

# places var1 as the row index and var2 as the col index
# able to see how promotions and non-promotion items sell according to product one price
table(store.df$p1price, store.df$p1prom)
      
         0   1
  2.19 354  41
  2.29 398  46
  2.49 381  42
  2.79 396  47
  2.99 343  32
plot((table(store.df$p1price, store.df$p1prom)))
plot((table(store.df$p2price, store.df$p2prom)))

#At each price level, Product 1 is observed to have been promoted approximately 10 % of the time (as expected, given how we created the data in Sect. 3.1.1). In fact, we can compute the exact fraction of times product 1 is on promotion at each price point, if we assign the table to a variable and then divide the second column of the table by the sum of the first and second columns:
p1.table2 <- table(store.df$p1price, store.df$p1prom)
p1.table2
      
         0   1
  2.19 354  41
  2.29 398  46
  2.49 381  42
  2.79 396  47
  2.99 343  32
# sum the two columns to get the total then divide it by second column (freq of promotions) to get the fraction or ratio of price/prom
p1.table2[, 2] / (p1.table2[, 1] + p1.table2[, 2])
      2.19       2.29       2.49       2.79 
0.10379747 0.10360360 0.09929078 0.10609481 
      2.99 
0.08533333 

The second command takes the second column of table p1.table—the column with counts of how often the product is promoted—and divides by the total count to get the proportion of times the product was promoted at each price point. R auto- matically applies math operators + and / across the entire columns.

By combining results in this way, you can easily produce exactly the results you want along with code that can repeat the analysis on demand. This is very helpful to marketing analysts who produce weekly or monthly reports for sales, web traffic, and the like.

3.2.2 Continuous Variables

Counts are useful when there is a small number of categories, but with continuous data it is more helpful to summarise the data in terms of its distribution.

describes <- c("Extremes", 'Central Tendency', 'Dispersion', 'Points')
functions <- c("min(x), max(x)", 'mean(x), median (x)', 'var(x), sd(x), IQR(x), mad(x)', 'quantile(x, probs=c(...))')
values <- c("Min Max Value", 'Arith Mean / Median', 'Variance around the mean, Stand. Dev, sqrt(var(x)), Interquartile range(75-25%), median absolute deviation (robust variance estimator)', 'Percentiles')
distfuncs <- data.frame(
  describes, 
  functions,
  values
)
table(distfuncs)
, , values = Arith Mean / Median

                  functions
describes          mean(x), median (x)
  Central Tendency                   1
  Dispersion                         0
  Extremes                           0
  Points                             0
                  functions
describes          min(x), max(x)
  Central Tendency              0
  Dispersion                    0
  Extremes                      0
  Points                        0
                  functions
describes          quantile(x, probs=c(...))
  Central Tendency                         0
  Dispersion                               0
  Extremes                                 0
  Points                                   0
                  functions
describes          var(x), sd(x), IQR(x), mad(x)
  Central Tendency                             0
  Dispersion                                   0
  Extremes                                     0
  Points                                       0

, , values = Min Max Value

                  functions
describes          mean(x), median (x)
  Central Tendency                   0
  Dispersion                         0
  Extremes                           0
  Points                             0
                  functions
describes          min(x), max(x)
  Central Tendency              0
  Dispersion                    0
  Extremes                      1
  Points                        0
                  functions
describes          quantile(x, probs=c(...))
  Central Tendency                         0
  Dispersion                               0
  Extremes                                 0
  Points                                   0
                  functions
describes          var(x), sd(x), IQR(x), mad(x)
  Central Tendency                             0
  Dispersion                                   0
  Extremes                                     0
  Points                                       0

, , values = Percentiles

                  functions
describes          mean(x), median (x)
  Central Tendency                   0
  Dispersion                         0
  Extremes                           0
  Points                             0
                  functions
describes          min(x), max(x)
  Central Tendency              0
  Dispersion                    0
  Extremes                      0
  Points                        0
                  functions
describes          quantile(x, probs=c(...))
  Central Tendency                         0
  Dispersion                               0
  Extremes                                 0
  Points                                   1
                  functions
describes          var(x), sd(x), IQR(x), mad(x)
  Central Tendency                             0
  Dispersion                                   0
  Extremes                                     0
  Points                                       0

, , values = Variance around the mean, Stand. Dev, sqrt(var(x)), Interquartile range(75-25%), median absolute deviation (robust variance estimator)

                  functions
describes          mean(x), median (x)
  Central Tendency                   0
  Dispersion                         0
  Extremes                           0
  Points                             0
                  functions
describes          min(x), max(x)
  Central Tendency              0
  Dispersion                    0
  Extremes                      0
  Points                        0
                  functions
describes          quantile(x, probs=c(...))
  Central Tendency                         0
  Dispersion                               0
  Extremes                                 0
  Points                                   0
                  functions
describes          var(x), sd(x), IQR(x), mad(x)
  Central Tendency                             0
  Dispersion                                   1
  Extremes                                     0
  Points                                       0
min(store.df$p1sales)
[1] 71
max(store.df$p2sales)
[1] 221
mean(store.df$p1prom)
[1] 0.1
median(store.df$p2sales)
[1] 97
var(store.df$p1sales)
[1] 820.4308
sd(store.df$p1sales)
[1] 28.64316
IQR(store.df$p1sales)
[1] 37
mad(store.df$p1sales)
[1] 26.6868
quantile(store.df$p1sales, probs=c(0.25, 0.5, 0.75))
25% 50% 75% 
113 130 150 

In the case of quantile() we have asked for the 25th, 50th, and 75th percentiles using the argument probs=c(0.25, 0.5, 0.75), which are also known as the median (50th percentile, same as the median() function) and the edges of the interquartile range, the 25th and 75th percentiles.

For skewed and asymmetric distributions that are common in marketing, such as unit sales or household income, the arithmetic mean() and standard deviation sd() may be misleading; in those cases, the median() and interquartile range (IQR(), the range of the middle 50 % of data) are often more useful to summarize a distribution.

0:10/10
 [1] 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

Suppose we wanted a summary of the sales for product 1 and product 2 based on their median and interquartile range. We might assemble these summary statistics into a data frame that is easer to read than the one-line-at-a-time output above. We create a data frame to hold our summary statistics and then populate it using functions from Table 3.1. We name the columns and rows, and fill in the cells with function values:

# created 2 by 2 dataframe
mysummary.df <- data.frame(matrix(NA, nrow=2, ncol=2))
# label the column names
names(mysummary.df) <- c("Median Sales", "IQR")
# label the indexes of the obs
rownames(mysummary.df) <- c("Product 1", "Product 2")
# select by row index and set median to ->
mysummary.df["Product 1", "Median Sales"] <- median(store.df$p1sales)
# select by row index and set median to ->
mysummary.df["Product 2", "Median Sales"] <- median(store.df$p2sales)
mysummary.df["Product 1", "IQR"] <- IQR(store.df$p1sales)
mysummary.df["Product 2", "IQR"] <- IQR(store.df$p2sales)
mysummary.df
          Median Sales IQR
Product 1          130  37
Product 2           97  30

Above: With this custom summary we can easily see that median sales are higher for prod- uct 1 (129 versus 96) and that the variation in sales of product 1 (the IQR across observations by week) is also higher.

3.3 Summarising Data Frames

it is tedious to apply them one at a time to columns of a large data frame

We describe three approaches: the basic summary() command, the describe() command from the psych package, and the R approach to iterating over variables with apply().

summary(store.df$Year, digits = 1)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
      1       1       2       2       2       2 

Use summary() after the data is imported and you want to check the quality

  • Check min and max for outliers
  • Check Mean and median are reasonable and similiar to each other

3.3.2 describe()

  • n
  • the count of observations;
  • median
  • mean
  • trimmed mean (the mean after dropping a small proportion of extreme values)
  • skew
  • kurtosis
  • mad
  • range
  • se

useful when interpreting data with regard to normal distributions.

library(psych)
describe(store.df)
          vars    n   mean    sd median trimmed   mad   min    max range  skew kurtosis   se
storeNum*    1 2080  10.50  5.77  10.50   10.50  7.41  1.00  20.00  19.0  0.00    -1.21 0.13
Year         2 2080   1.50  0.50   1.50    1.50  0.74  1.00   2.00   1.0  0.00    -2.00 0.01
Week         3 2080  26.50 15.01  26.50   26.50 19.27  1.00  52.00  51.0  0.00    -1.20 0.33
p1sales      4 2080 133.07 28.64 130.00  131.13 26.69 71.00 254.00 183.0  0.70     0.57 0.63
p2sales      5 2080 100.06 24.11  97.00   97.88 22.24 52.00 221.00 169.0  1.00     1.56 0.53
p1price      6 2080   2.54  0.29   2.49    2.53  0.44  2.19   2.99   0.8  0.28    -1.44 0.01
p2price      7 2080   2.70  0.33   2.59    2.69  0.44  2.29   3.19   0.9  0.32    -1.40 0.01
p1prom       8 2080   0.10  0.30   0.00    0.00  0.00  0.00   1.00   1.0  2.66     5.10 0.01
p2prom       9 2080   0.14  0.35   0.00    0.05  0.00  0.00   1.00   1.0  2.09     2.38 0.01
country*    10 2080   4.55  1.72   4.50    4.62  2.22  1.00   7.00   6.0 -0.29    -0.81 0.04
print("")
[1] ""
# if you have non-numeric factor variables you'll get an error, to resolve that pass in specific columns
describe(store.df[ , c(2, 4:9)])
        vars    n   mean    sd median trimmed   mad   min    max range skew kurtosis   se
Year       1 2080   1.50  0.50   1.50    1.50  0.74  1.00   2.00   1.0 0.00    -2.00 0.01
p1sales    2 2080 133.07 28.64 130.00  131.13 26.69 71.00 254.00 183.0 0.70     0.57 0.63
p2sales    3 2080 100.06 24.11  97.00   97.88 22.24 52.00 221.00 169.0 1.00     1.56 0.53
p1price    4 2080   2.54  0.29   2.49    2.53  0.44  2.19   2.99   0.8 0.28    -1.44 0.01
p2price    5 2080   2.70  0.33   2.59    2.69  0.44  2.29   3.19   0.9 0.32    -1.40 0.01
p1prom     6 2080   0.10  0.30   0.00    0.00  0.00  0.00   1.00   1.0 2.66     5.10 0.01
p2prom     7 2080   0.14  0.35   0.00    0.05  0.00  0.00   1.00   1.0 2.09     2.38 0.01
  • compared trimmed mean to overall mean
    • next to factor variables

3.3.4 apply()*

apply(x=DATA, MARGIN=MARGIN, FUN=FUNCTION) 

In R the term margin is a two-dimensional metaphor that denotes which “direc- tion” you want to do something: either along the rows (MARGIN=1) or columns (MARGIN=2), or both simultaneously (MARGIN=c(1, 2)).

apply(store.df[,2:9], MARGIN=2, FUN=mean)
       Year        Week     p1sales     p2sales     p1price 
  1.5000000  26.5000000 133.0668269 100.0634615   2.5443750 
    p2price      p1prom      p2prom 
  2.6995192   0.1000000   0.1384615 

Above: As it happens, colMeans() does the same thing as the command above, but apply gives you the flexibility to apply any function you like. If we want the row means instead, we simply change the margin to 1:

If we want the row means


apply(store.df[,2:9], 1, mean)
# by adding 1 as the second parameter, which is now margin (margin = 1) it will take the rows

#Similarly, we might find the sum() or sd() for multiple columns with margin=2:
apply(store.df[,2:9], 2, sum)
    Year     Week  p1sales  p2sales  p1price  p2price 
  3120.0  55120.0 276779.0 208132.0   5292.3   5615.0 
  p1prom   p2prom 
   208.0    288.0 
apply(store.df[,2:9], 2, sd)
      Year       Week    p1sales    p2sales    p1price 
 0.5001202 15.0119401 28.6431639 24.1071803  0.2948819 
   p2price     p1prom     p2prom 
 0.3292181  0.3000721  0.3454668 

we are checking data and wish to know the difference between the mean and median of each variable, perhaps to flag skew in the data.

apply(store.df[,2:9], 2, function(x) { mean(x) - median(x) } )
     Year      Week   p1sales   p2sales   p1price 
0.0000000 0.0000000 3.0668269 3.0634615 0.0543750 
  p2price    p1prom    p2prom 
0.1095192 0.1000000 0.1384615 

Apply is a functional method, people that are use to procedural programming might envision solving this with a for loop. R is primarily a functional programming language. One must try to think in terms of functions that are applied across data as we do here.

mysummary2.df <- data.frame(matrix(NA, nrow=2,ncol=2))
names(mysummary2.df) <- c("Median Sales", "IQR")
rownames(mysummary2.df) <- names(store.df)[4:5] # names from the data frame
mysummary2.df
        Median Sales IQR
p1sales           NA  NA
p2sales           NA  NA
mysummary2.df[, "Median Sales"] <- apply(store.df[,4:5], 2, median)
mysummary2.df[, "IQR"] <- apply(store.df[, 4:5], 2, IQR)
mysummary2.df
        Median Sales IQR
p1sales          130  37
p2sales           97  30

If there were many products instead of just two, the code would still work if we changed the number of allocated rows, and apply() would run automatically across all of them.

mysummary2.df <- data.frame(matrix(NA, nrow=2,ncol=4))
names(mysummary2.df) <- c("Median Sales", "IQR", "Mean Sales", "kurtosi")
rownames(mysummary2.df) <- names(store.df)[4:5] # names from the data frame
mysummary2.df
        Median Sales IQR Mean Sales kurtosi
p1sales           NA  NA         NA      NA
p2sales           NA  NA         NA      NA
mysummary2.df[, "Median Sales"] <- apply(store.df[,4:5], 2, median)
mysummary2.df[, "IQR"] <- apply(store.df[, 4:5], 2, IQR)
mysummary2.df[, "Mean Sales"] <- apply(store.df[, 4:5], 2, mean)
mysummary2.df[, "kurtosi"] <- apply(store.df[, 4:5], 2, psych::kurtosi)
mysummary2.df
        Median Sales IQR Mean Sales   kurtosi
p1sales          130  37   133.0668 0.5653844
p2sales           97  30   100.0635 1.5617362

3.4 Single Variable Visualisation

hist(store.df$p1sales)
#add title and axis labels
 hist(store.df$p1sales,
  main="Product 1 Weekly Sales Frequencies, All Stores",
 xlab="Product 1 Sales (Units)",
 ylab="Count" )

 
 
 # increase granularity by adding breaks         
hist(store.df$p1sales,
     main="Product 1 Weekly Sales Frequencies, All Stores",
      xlab="Product 1 Sales (Units)",
      ylab="Count",
      breaks=30,             # more columns
      col="lightblue")       # color the bars 

NA

The y-axis value for the height of the bars changes according to count. The count depends on the number of bins and on the sample size. We can make it absolute by using relative frequencies (technically, the density estimate) instead of counts for each point. This makes the Y axis comparable across different sized sample

hist(store.df$p1sales,
      main="Product 1 Weekly Sales Frequencies, All Stores",
      xlab="Product 1 Sales (Units)",
    ylab="Relative frequency",
 breaks=30,
      col="lightblue",
      freq=FALSE,                  # freq=FALSE means plot density, not counts
      xaxt="n")                    # xaxt="n" means "x axis tick marks == no"
lines(density(store.df$p1sales, bw=10),    # "bw= ..." adjusts the smoothing
       type="l", col="darkred", lwd=2)      # lwd = line width

Finally, we add a smoothed estimation line. To do this, we use the density() function to estimate density values for the p1sales vector, and add those to the chart with the lines() command. The lines() command adds elements to the current plot in the same way we saw above for the axis command.

3.4.2 Boxplots

boxplot(store.df$p2sales, xlab="Weekly sales", ylab="P2",
          main="Weekly sales of P2, All stores", horizontal=TRUE)

Boxplots are even more useful when you compare distributions by some other factor. How do different stores compare on sales of product 2? The boxplot() command makes it easy to compare these by specifying a response formula using tilde notation, where the tilde (“∼”) separates the response variable (sometimes called a dependent variable) from the explanatory variable (sometimes rather misleadingly called an independent variable). In this case, our response variable is p2sales and we want to plot it with regard to the explanatory variable storeNum. This may be easiest to understand with the R code:

# boxplot p2sales by Store.
boxplot(store.df$p2sales ~ store.df$storeNum, horizontal=TRUE,
  ylab="Store", xlab="Weekly unit sales", las=1,
  main="Weekly Sales of P2 by Store")

Above: The first portion of the command may be read as “boxplot p2sales by Store.” For- mulas like this are pervasive in R and are used both for plotting and for estimating models. We discuss formulas in detail in Sect. 5.2.1 and Chap. 7.

We added one other argument to the plot: las=1. That forces the axes to have text in the horizontal direction, making the store numbers more readable. The result is Fig. 3.8, where stores are roughly similar in sales of product 2 (this is not a statistical test of difference, just a visualization).



# but do p2 sales differ in relation to in-store promotion
boxplot(p2sales ~ p2prom, data=store.df, horizontal=TRUE, yaxt="n",
  ylab="P2 promoted in store?", xlab="Weekly sales",
  main="Weekly sales of P2 with and without promotion")
  axis(side=4, at=c(1,2), labels=c("No", "Yes"))

3.4.3 QQ Plot to Check Normality

evaluate a distribution more formally

Quantile–quantile (QQ) plots are a good way to check one’s data against a distribution that you think it should come from. Some common statistics such as the correlation coefficient r (to be precise, the Pearson product-moment correlation coefficient) are interpreted under an assumption that data are normally distributed. A QQ plot can confirm that the distribution is, in fact, normal by plotting the observed quantiles of your data against the quantiles that would be expected for a normal distribution.

To do this, the qqnorm() command compares data vs. a normal distribution; you can use qqline() to add a diagonal line for easier reading. We check p1sales to see whether it is normally distributed:

qqnorm(store.df$p1sales)
#make sure you call a plot before calling qqline
qqline(store.df$p1sales)

far from the lines at the end which implies the data is not normally distributed

The upward curving shape is typical of data with high positive skew

What do you do?? You might transform your daya. (logarithmic dist…)

qqnorm(log(store.df$p1sales))
?qqnorm
#make sure you call a plot before calling qqline
qqline(log(store.df$p1sales))

the points are much closer to the line especially on the higher end implying that the log transform is more consistent with the normal distribution

When testing assumptions about your data, qqnorm can be very useful. Research more on how to interpret them.

Cumulative Distribution

formally called - empirical cumulative distribution function (ECDF)

Next:

We plot the ECDF of p1sales by combining a few steps. First, we use the ecdf() function to find the ECDF of the data. Then we wrap plot() around that, adding options such as titles. Next we put some nicer-looking labels on the Y axis that relabel the proportions as percentiles. The paste() function combines a number vector (0, 10, 20, …) with the “%” symbol to make each label.

plot(ecdf(store.df$p1sales))
plot(ecdf(store.df$p1sales),
      main="Cumulative distribution of P1 Weekly Sales",
      ylab="Cumulative Proportion",
      xlab=c("P1 weekly sales, all stores", "90% of weeks sold <= 171 units"),
      yaxt="n")

 axis(side=2, at=seq(0, 1, by=0.1), las=1,
      labels=paste(seq(0,100,by=10), "%", sep=""))
 abline(h=0.9, lty=3)           # "h=" for horizontal line; "lty=3" for dotted
 abline(v=quantile(store.df$p1sales, pr=0.9), lty=3)  # "v=" for vertical line

3.4.5 Language Brief: by() and aggregate()

What should we do if we want to break out data by factors and summarize it, a process you might know as “cross-tabs” or “pivot tables”?

For example, how can we compute the mean sales by store?

by and aggregate can help!

Suppose we wish to find the average sales of P1 by store

The DATA would be the weekly sales for each store, store.df\(p1sales. We wish to split this by store, so the INDICES (actually, “index” in this case) would be store.df\)storeNum. Finally, we get the average of each of those groups by using the mean function.

# Suppose we wish to find the average sales of P1 by store
by(store.df$p1sales, store.df$storeNum, mean)
by(store.df$p1sales, list(store.df$storeNum, store.df$Year), mean)

How does this work?(BELOW) Just as with by(), aggregate(x=DATA, by=BY, FUN=FUNCTION) applies a particular function (FUN) according to divisions of the data specified by a factor (by). We want to find the total sales by country, so we apply the mean function by store.df$country.

aggregate(store.df$p1sales, by=list(country=store.df$country), sum)
  country     x
1      AU 14583
2      BR 27864
3      CN 27411
4      DE 68928
5      GB 41103
6      JP 55303
7      US 41587

3.4.6 Maps

marketing data on a map - choropleth map

Here is a routine example. Suppose that we want to chart the total sales by coun- try. We use aggregate() as in Sect. 3.4.5 to find the total sales of P1 by country:

 p1sales.sum <- aggregate(store.df$p1sales,
                         by=list(country=store.df$country), sum)

install.packages(c("rworldmap", "RColorBrewer"))  # if needed
library(rworldmap)
library(RColorBrewer)

First, we have to associate the aggregated data with specific map regions using the country codes. This can be done with the joinCountryData2Map() func- tion, which matches country locations (store.df$country) for data points with the corresponding international standard names (ISO names) and returns a map object:

p1sales.map <- joinCountryData2Map(p1sales.sum, joinCode = "ISO2",
                                     nameJoinColumn = "country")
Error: could not find function "joinCountryData2Map"

We acknowledge the need for caution despite the popularity of such maps.

3.6 Key Points

The following guidelines and pointers will help you to describe data accurately and quickly:

  • Consider simulating data before collecting it, in order to test your assumptions and develop initial analysis code (Sect. 3.1).
  • Always check your data for proper structure and data quality using str(), head(), summary(), and other basic inspection commands (Sect. 3.3.3).
  • Describe discrete (categorical) data with table() (Sect. 3.2.1) and inspect continuous data with describe() from the psych package (Sect. 3.3.2).
  • Histograms (Sect. 3.4.1) and boxplots (Sect. 3.4.2) are good for initial data visualization.
  • Use by() and aggregate() to break out your data by grouping variables (Sect. 3.4.5).
  • Advanced visualization methods include cumulative distribution (Sect. 3.4.4), normality checks (Sect. 3.4.3), and mapping (Sect. 3.4.6).

4 Relationships Between Continous Variables

4.1.1

we create a data set for 1,000 customers of a retailer who sells products in stores and online. This data is typical of what one might sample from a company’s customer relationship management (CRM) system. We begin by setting a random number seed to make the process repeatable (as described in Sect. 3.1.2) and creating a data frame to store the data:

set.seed(21821)
ncust <- 1000
cust.df <- data.frame(cust.id=as.factor(c(1:ncust)))

Next we create a number of variables describing the customers, add those variables to the cust.df data frame, and inspect them with summary():

cust.df$age <- rnorm(n=ncust, mean=35, sd=5)
cust.df$credit.score <- rnorm(n=ncust, mean=3*cust.df$age+620, sd=50)
cust.df$email <- factor(sample(c("yes", "no"), size=ncust, replace=TRUE,
                             prob=c(0.8, 0.2)))
cust.df$distance.to.store <- exp(rnorm(n=ncust, mean=2, sd=1.2))
summary(cust.df)
    cust.id         age         credit.score   email     distance.to.store 
 1      :  1   Min.   :19.34   Min.   :543.0   no :186   Min.   :  0.2136  
 2      :  1   1st Qu.:31.43   1st Qu.:691.7   yes:814   1st Qu.:  3.3383  
 3      :  1   Median :35.10   Median :725.5             Median :  7.1317  
 4      :  1   Mean   :34.92   Mean   :725.5             Mean   : 14.6553  
 5      :  1   3rd Qu.:38.20   3rd Qu.:757.2             3rd Qu.: 16.6589  
 6      :  1   Max.   :51.86   Max.   :880.8             Max.   :267.0864  
 (Other):994                                                               

Our final variable for the basic CRM data is distance.to.store, which we assume follows the exponential of the normal distribution. That gives dis- tances that are all positive, with many distances that are relatively close to the nearest store and fewer that are far from a store. To see the distribution for yourself, try hist(cust.df$distance.to.store).

Formally, we say that distance.to.store follows a lognormal distribution. (This is sufficiently common that there is a built-in function called rlnorm(n, meanlog, sdlog) that does the same thing as taking the exponential of rnorm().)

4.1.2 Simulating Online and In-Store Sales Data

Our next step is to create data for the online store: 1 year totals for each customer for online visits and transactions, plus total spending. We simulate the number of visits with a negative binomial distribution, a discrete distribution often used to model counts of events over time. Like the lognormal distribution, the negative binomial distribution generates positive values and has a long right-hand tail, meaning that in our data most customers make relatively few visits and a few customers make many visits. Data from the negative binomial distribution can be generated using rnbinom():

cust.df$online.visits
   [1]  20 121  39   1  35   1   1  48   0  14   2   0   0 108   0   1   0  33  16  13   0
  [22]  24   0  14   3  83 127   8  30  40   0  13 197   4   0   0  21   0   1   0   0   0
  [43]  17   0   0  43   1 111   0   1   0  24  33  24  19   0  85   0   7   0  67   0   0
  [64]   1  34  14  21  11   0   0   2   4   0   1  12   5   1   3   0   2   0   0   0   0
  [85]  45   4   2   2   0  40   6   0   7  12   5 144   1 143   5 186  52   2   0  30   4
 [106] 112   1   0   5  42  18   0   1 323  30   3   0   7   0  34   0  15  64  46   2  19
 [127]  71  10   4  77 106  79   2 218   5   0  19   3  36   0  99   6   0   0   0  22   0
 [148]   0   0  26  12   0 118   5   0   0   0   0 102  20   6   9   0   5   0  13  18   0
 [169]   0   0   0  26   6 236   0   9   1   0  59  89  17  28  88   0   5  13  62  13  21
 [190]   5 134  10   0  12   1   5  27  32   0 122   0   1   0  73   3  11   0  16  50   3
 [211]   0  10   1  28  17   0   5  25  55   0  20  11   0  18 156   2   0  32   0   5  36
 [232]  18   0  53  19   3   2   2  81  22   1   9   3   1  31   6   0   0   2   2   2  27
 [253]   6   0   4   2  10 167  15   9  43   0  62 121   2   0  78  98  98   2  97  10  12
 [274]   4   3  47  10   8   0  87  10  39   9   0  13   1  28  28   1   4  16 323   0 184
 [295]  20   0  11  16  21   0 151   0   1 147   5 113  31  13   0   2  84  40   0   1 156
 [316]   0   3   3   2   0   0  26  64  10   2  84   6   0   0  14  83  10  55   8  43 186
 [337]   3   2   0   0   5  11   0  12   0   0  18   7 180  14   0   4   2   1   0  16  18
 [358] 111  19   1  44   4  10 231 152  29  44 102 111  10   4   0   6   3   2   0  39   0
 [379]   0   3  75   0   0  18   7  28   0  40  13   2   0   1   0  33  30   2  14  86  10
 [400]   0   9   0  10  77  87  86  26  52 124   5   0   3  62  11   1   0  15  16   1   0
 [421]   8  50 266  55  24   0   5   1  62   0   0   0   0   7   0  15  86  26  41   0   0
 [442]   1   2  48 145   4  28  60  52   2   2  42  12   0  27   0   0   0 131 115   2   7
 [463]  48 114 291   7  99  94   2  10   0  49   2   1   1  46   0   7  12   2   2 194 104
 [484]  12 162   0   1   1   0   4   0   9   0   0  14  14   0  18   0  14   0   1   6   9
 [505]   0   0   0   0  15   2  11  58   4   3  14  19   7   0   1   0  68   2   0   1  25
 [526]   4  31   3  37  15   0  40   6  50   2 108   0   1   4  49   2  58  24  12   0   9
 [547]   0   0   0   0   4   0   0   0   1  35   5   2   0  61  65   0  95   1  48  42   0
 [568]   8  12   1   4   0  97  72 100  22   0  62   6  40  25   0   0  14   0   0   0   1
 [589]   4   0 110   2   1   4  23  11  85  17 138 109   0  28  27   5   0 100   0  65   9
 [610]  93   2   0  95   0 606   0   0   2   7  31 108 111   0   0   4 530  31  73  56   0
 [631]  33   6   2  58  14  78  52   2  15   6   0   9  27  54  44   3   0   2   0  17   0
 [652]   6 185  12  95   3   3   0   9   6  40 119 132  25   0   5  11   0   0 136  11  16
 [673]   5   6   6   0   1   0   0   5  64   0  12  66   2  20  15   1   7   3   2  16 131
 [694]   0 118  28  59  42   1   4  23  24   0   0   6   0  97  35 143  26   0   0   0   3
 [715]  99   0  50   3   3   0   0   1   0   1  48   0  95  12   0   1  21   3   8  31   2
 [736]  11   0  53 153  56   0   1 328   0   3 139 108   0  40   8   4   1  22   0  14 113
 [757]  29   1   3   7  93  11   0  42  29   3   3   3 378  30   0  85  18   0 130  73  12
 [778]   0   2   0 181   0  27   1  16  25 217  42   0   0   6  62   0   0   0   0  19  15
 [799]   0   0 123  37   0   0   4   2   6   0   0  31   8  72   5 145   0   0 315   1   0
 [820]  59   1  18  19 161  18  87   5  52   3   1   0  84  84   2 167   0  35  53 241  24
 [841]   2 139  26   0  75   0   0   0   0   8   3  25  11   0 146  22  13  28 181   3   5
 [862]   0  78   0   0  23   0   1  12   1   0   0  93 178  11   0   0   8  31   0   8  50
 [883]   1   1  16   7  23   0   0   6  13   7  88  10   9   0   0  16   2   0   0  10   8
 [904]  10 101  17   2   6   0   2   1   9   2   0  36  15  21   5   7   0  37  93   0   0
 [925]   0  61   7   7   0   0   2  24   0  62   3   0  71  24   0  12   4   2   1   0   1
 [946]   7   0   2   5   0   3  13  45   0   2  22  51 107  39  17  59  17  80  39   0   3
 [967]   0   0 121  28  12   8 102   5  20   2  85   0   1   6   3  11   0   1  75  83   0
 [988]  28   0   0  28   1  49   5   3   9  46  16  47  37

Above: We model the mean (mu) of the negative binomial with a baseline value of 15. The size argument sets the degree of dispersion (variation) for the samples. We add an average 15 online visits for customers who have an email on file, using ifelse() to generate a vector of 0 or 15 as appropriate. Finally, we add or subtract visits from the target mean based on the customer’s age relative to the sample median; customers who are younger are simulated to make more online visits. To see exactly how this works, try cutting and pasting pieces of the code above into the R console.

For each online visit that a customer makes, we assume there is a 30 % chance of placing an order and use rbinom() to create the variable online.trans. We assume that amounts spent in those orders (the variable online.spend) are lognormally distributed:

 cust.df$online.trans <- rbinom(ncust, size=cust.df$online.visits, prob=0.3)
 cust.df$online.spend <- exp(rnorm(ncust, mean=3, sd=0.1)) *
                         cust.df$online.trans
 
 hist(cust.df$online.trans)
 hist(cust.df$online.spend)

Above: The random value for amount spent per transaction—sampled with exp(rnorm()) is multiplied by the variable for number of transactions to get the total amount spent.

Next we generate in-store sales data similarly, except that we don’t generate a count of store visits; few customers visit a physical store without making a purchase and even if customers did visit without buying, the company probably couldn’t track the visit. We assume that transactions follow a negative binomial distribution, with lower average numbers of visits for customers who live farther away. We model in-store spending as a lognormally distributed variable simply multiplied by the number of transactions:

# lower average number of visits if they live further away
cust.df$store.trans <- rnbinom(ncust, size=5,
                                mu=3 / sqrt(cust.df$distance.to.store))
# lognormally distributed var * by the number of transactions
 cust.df$store.spend <- exp(rnorm(ncust, mean=3.5, sd=0.4)) *
                        cust.df$store.trans
 
 
 hist(cust.df$store.trans, plot = TRUE)
 summary(cust.df)
    cust.id         age         credit.score   email     distance.to.store 
 1      :  1   Min.   :19.34   Min.   :543.0   no :186   Min.   :  0.2136  
 2      :  1   1st Qu.:31.43   1st Qu.:691.7   yes:814   1st Qu.:  3.3383  
 3      :  1   Median :35.10   Median :725.5             Median :  7.1317  
 4      :  1   Mean   :34.92   Mean   :725.5             Mean   : 14.6553  
 5      :  1   3rd Qu.:38.20   3rd Qu.:757.2             3rd Qu.: 16.6589  
 6      :  1   Max.   :51.86   Max.   :880.8             Max.   :267.0864  
 (Other):994                                                               
 online.visits     online.trans      online.spend      store.trans      store.spend    
 Min.   :  0.00   Min.   :  0.000   Min.   :   0.00   Min.   : 0.000   Min.   :  0.00  
 1st Qu.:  0.00   1st Qu.:  0.000   1st Qu.:   0.00   1st Qu.: 0.000   1st Qu.:  0.00  
 Median :  6.00   Median :  2.000   Median :  38.05   Median : 1.000   Median : 28.63  
 Mean   : 28.29   Mean   :  8.458   Mean   : 170.44   Mean   : 1.238   Mean   : 44.56  
 3rd Qu.: 31.00   3rd Qu.:  9.000   3rd Qu.: 176.51   3rd Qu.: 2.000   3rd Qu.: 59.63  
 Max.   :606.00   Max.   :184.000   Max.   :3630.99   Max.   :19.000   Max.   :617.97  
                                                                                       
 hist(cust.df$store.spend, plot = TRUE)

 
 summary(cust.df)
    cust.id         age         credit.score   email     distance.to.store 
 1      :  1   Min.   :19.34   Min.   :543.0   no :186   Min.   :  0.2136  
 2      :  1   1st Qu.:31.43   1st Qu.:691.7   yes:814   1st Qu.:  3.3383  
 3      :  1   Median :35.10   Median :725.5             Median :  7.1317  
 4      :  1   Mean   :34.92   Mean   :725.5             Mean   : 14.6553  
 5      :  1   3rd Qu.:38.20   3rd Qu.:757.2             3rd Qu.: 16.6589  
 6      :  1   Max.   :51.86   Max.   :880.8             Max.   :267.0864  
 (Other):994                                                               
 online.visits     online.trans      online.spend      store.trans      store.spend    
 Min.   :  0.00   Min.   :  0.000   Min.   :   0.00   Min.   : 0.000   Min.   :  0.00  
 1st Qu.:  0.00   1st Qu.:  0.000   1st Qu.:   0.00   1st Qu.: 0.000   1st Qu.:  0.00  
 Median :  6.00   Median :  2.000   Median :  38.05   Median : 1.000   Median : 28.63  
 Mean   : 28.29   Mean   :  8.458   Mean   : 170.44   Mean   : 1.238   Mean   : 44.56  
 3rd Qu.: 31.00   3rd Qu.:  9.000   3rd Qu.: 176.51   3rd Qu.: 2.000   3rd Qu.: 59.63  
 Max.   :606.00   Max.   :184.000   Max.   :3630.99   Max.   :19.000   Max.   :617.97  
                                                                                       

4.1.3 Simulating Satisfaction Survey Responses

To simulate survey responses, we assume that each customer has an unobserved overall satisfaction with the brand. We generate this overall satisfaction from a nor- mal distribution:

We assume that overall satisfaction is a psychological construct that is not directly observable. Instead, the survey collects information on two items: satisfaction with service, and satisfaction with the selection of products. We assume that customers’ responses to the survey items are based on unobserved levels of satisfaction overall (sometimes called the “halo” in survey response) plus the specific levels of satisfac- tion with the service and product selection.

To create such a score from a halo variable, we add sat.overall (the halo) to a random value specific to the item, drawn using rnorm(). Because survey responses are typically given on a discrete, ordinal scale (i.e., “very unsatisfied”, “unsatisfied”, etc.), we convert our continuous random values to discrete integers using the floor() function.

summary(cbind(sat.service, sat.selection))
  sat.service    sat.selection 
 Min.   :1.000   Min.   :0.00  
 1st Qu.:3.000   1st Qu.:2.00  
 Median :3.000   Median :2.00  
 Mean   :3.087   Mean   :2.45  
 3rd Qu.:4.000   3rd Qu.:3.00  
 Max.   :6.000   Max.   :5.00  
hist(sat.service)

hist(sat.selection)

Note that we use cbind() to temporarily combine our two vectors of data into a matrix, so that we can get a combined summary with a single line of code. The sum- mary shows that our data now ranges from −1 to 6. However, a typical satisfaction item might be given on a 5-point scale. To fit that, we replace values that are greater than 5 with 5, and values that are less than 1 with 1. This enforces the floor and ceiling effects often noted in survey response literature.

We set the ceiling by indexing with a vector that tests whether each element of sat.serviceisgreaterthan5):sat.service[sat.service > 5].This might be read as “sat.service, where sat.service is greater than 5.” For the elements that are selected—which means that the expression evaluates as TRUE—we replace the current values with the ceiling value of 5. We do the same for the floor effects (< 1,replacingwith1)andlikewisefortheceilingandfloorofsat.selection. While this sounds quite complicated, the code is simple:

# should be read as sat.service, where sat.service is greater than 5, set to 5
sat.service[sat.service > 5] <- 5
# should be read as sat.service, where sat.service is less than 1, set to 1
sat.service[sat.service < 1] <- 1
sat.selection[sat.selection > 5] <- 5
sat.selection[sat.selection < 1] <- 1
summary(cbind(sat.service, sat.selection))
  sat.service    sat.selection  
 Min.   :1.000   Min.   :1.000  
 1st Qu.:3.000   1st Qu.:2.000  
 Median :3.000   Median :2.000  
 Mean   :3.086   Mean   :2.462  
 3rd Qu.:4.000   3rd Qu.:3.000  
 Max.   :5.000   Max.   :5.000  

4.1.4 Simulating Non-Response Data

Because some customers do not respond to surveys, we eliminate the simulated an- swers for a subset of respondents who are modeled as not answering. We do this by creating a variable of TRUE and FALSE values called no.response and then as- signing a value of NA for the survey response for customers whose no.response is TRUE. As we have discussed, NA is R’s built-in constant for missing data.

We model non-response as a function of age, with higher likelihood of not responding to the survey for older customers:

no.response <- as.logical(rbinom(ncust, size=1, prob=cust.df$age/100))
sat.service[no.response] <- NA
sat.selection[no.response] <- NA
summary(cbind(sat.service, sat.selection))
  sat.service    sat.selection 
 Min.   :1.000   Min.   :1.00  
 1st Qu.:2.000   1st Qu.:2.00  
 Median :3.000   Median :2.00  
 Mean   :3.077   Mean   :2.43  
 3rd Qu.:4.000   3rd Qu.:3.00  
 Max.   :5.000   Max.   :5.00  
 NA's   :353     NA's   :353   

summary recognises that 341 customers with NA values and excludes them from the statistics

Then we add the survey responses to cust.df and clean up the workspace.

cust.df$sat.service <- sat.service
cust.df$sat.selection <- sat.selection
summary(cust.df)

We are ready for analysis!

4.2 Exploring Associations Between Variables with Scatterplots

str(cust.df)
'data.frame':   1000 obs. of  10 variables:
 $ cust.id          : Factor w/ 1000 levels "1","2","3","4",..: 1 2 3 4 5 6 7 8 9 10 ...
 $ age              : num  22.9 28 35.9 30.5 38.7 ...
 $ credit.score     : num  631 749 733 830 734 ...
 $ email            : Factor w/ 2 levels "no","yes": 2 2 2 2 1 2 2 2 1 1 ...
 $ distance.to.store: num  2.58 48.18 1.29 5.25 25.04 ...
 $ online.visits    : num  20 121 39 1 35 1 1 48 0 14 ...
 $ online.trans     : int  7 36 13 1 4 0 0 14 0 4 ...
 $ online.spend     : num  135.1 739.2 219.7 18.6 79.3 ...
 $ store.trans      : num  0 0 3 1 0 1 3 0 0 0 ...
 $ store.spend      : num  0 0 99.8 26.1 0 ...

Additional variables report 1-year total visits to the online site

4.2.1 Scatterplot

plot(x=cust.df$age, y=cust.df$credit.score)
plot(cust.df$age, cust.df$credit.score,
     col="blue",
     xlim=c(15, 55), ylim=c(500, 900),
     main="Active Customers as of June 2014",
     xlab="Customer Age (years)", ylab="Customer Credit Score ")

abline(h=mean(cust.df$credit.score), col="dark blue", lty="dotted")
abline(v=mean(cust.df$age), col="dark blue", lty="dotted")

R looks at what type of data you are trying to plot and, based on the data type, R will choose a spe- cific lower-level plotting function, known as a method, that is appropriate to the data you are trying to plot. When we call plot() with vectors of x and y coordinates, R uses the plot.default() function. However, there are many other plotting functions for different data types. For example, if you plot the cust.df data frame by typing plot(cust.df) into the console, R will use plot.data.frame() instead of plot.default(). This produces one of several plot types depending on the number of dimensions in the data frame; in this case, it produces a scatterplot matrix, which we review in Sect. 4.4.2.

t may be because R has selected a different method than you expect. If so, check the data types of the vari- ables you’re sending to plot() because R uses those to select a plot method.

methods(plot)
  [1] plot,allcategorical_missing_data.frame,binary-method     
  [2] plot,allcategorical_missing_data.frame,categorical-method
  [3] plot,ANY,ANY-method                                      
  [4] plot,color,ANY-method                                    
  [5] plot,mi_list,ANY-method                                  
  [6] plot,mi,ANY-method                                       
  [7] plot,missing_data.frame,binary-method                    
  [8] plot,missing_data.frame,categorical-method               
  [9] plot,missing_data.frame,missing_variable-method          
 [10] plot,missing_data.frame,semi-continuous-method           
 [11] plot,profile.mle,missing-method                          
 [12] plot.aareg*                                              
 [13] plot.acf*                                                
 [14] plot.ACF*                                                
 [15] plot.agnes*                                              
 [16] plot.areg*                                               
 [17] plot.areg.boot*                                          
 [18] plot.aregImpute*                                         
 [19] plot.augPred*                                            
 [20] plot.balance*                                            
 [21] plot.biVar*                                              
 [22] plot.boot*                                               
 [23] plot.cld*                                                
 [24] plot.clusGap*                                            
 [25] plot.coef.mer*                                           
 [26] plot.cohesiveBlocks*                                     
 [27] plot.communities*                                        
 [28] plot.compareFits*                                        
 [29] plot.confint.glht*                                       
 [30] plot.correspondence*                                     
 [31] plot.cox.zph*                                            
 [32] plot.curveRep*                                           
 [33] plot.data.frame*                                         
 [34] plot.decomposed.ts*                                      
 [35] plot.default                                             
 [36] plot.dendrogram*                                         
 [37] plot.density*                                            
 [38] plot.diana*                                              
 [39] plot.drawPlot*                                           
 [40] plot.ecdf                                                
 [41] plot.factor*                                             
 [42] plot.formula*                                            
 [43] plot.function                                            
 [44] plot.gam*                                                
 [45] plot.gbayes*                                             
 [46] plot.ggplot*                                             
 [47] plot.glht*                                               
 [48] plot.gls*                                                
 [49] plot.grenander*                                          
 [50] plot.gtable*                                             
 [51] plot.hclust*                                             
 [52] plot.histogram*                                          
 [53] plot.HoltWinters*                                        
 [54] plot.huge*                                               
 [55] plot.igraph*                                             
 [56] plot.InformativeTesting*                                 
 [57] plot.intervals.lmList*                                   
 [58] plot.irt                                                 
 [59] plot.isoreg*                                             
 [60] plot.jam*                                                
 [61] plot.lda*                                                
 [62] plot.lm*                                                 
 [63] plot.lme*                                                
 [64] plot.lmList*                                             
 [65] plot.lmList4*                                            
 [66] plot.lmList4.confint*                                    
 [67] plot.mca*                                                
 [68] plot.mcmc*                                               
 [69] plot.mcmc.list*                                          
 [70] plot.medpolish*                                          
 [71] plot.merMod*                                             
 [72] plot.mlm*                                                
 [73] plot.mona*                                               
 [74] plot.monoreg*                                            
 [75] plot.nffGroupedData*                                     
 [76] plot.nfnGroupedData*                                     
 [77] plot.nls*                                                
 [78] plot.nmGroupedData*                                      
 [79] plot.partition*                                          
 [80] plot.PBmodcomp*                                          
 [81] plot.pdMat*                                              
 [82] plot.poly                                                
 [83] plot.poly.parallel                                       
 [84] plot.powerTransform*                                     
 [85] plot.ppr*                                                
 [86] plot.prcomp*                                             
 [87] plot.princomp*                                           
 [88] plot.profile*                                            
 [89] plot.profile.nls*                                        
 [90] plot.psych                                               
 [91] plot.qgraph*                                             
 [92] plot.qss1*                                               
 [93] plot.qss2*                                               
 [94] plot.Quantile2*                                          
 [95] plot.ranef.lme*                                          
 [96] plot.ranef.lmList*                                       
 [97] plot.ranef.mer*                                          
 [98] plot.raster*                                             
 [99] plot.residuals                                           
[100] plot.ridgelm*                                            
[101] plot.rm.boot*                                            
[102] plot.roc*                                                
[103] plot.rpart*                                              
[104] plot.rq.process*                                         
[105] plot.rqs*                                                
[106] plot.rqss*                                               
[107] plot.select*                                             
[108] plot.shingle*                                            
[109] plot.silhouette*                                         
[110] plot.sim*                                                
[111] plot.simulate.lme*                                       
[112] plot.sir*                                                
[113] plot.skewpowerTransform*                                 
[114] plot.spec*                                               
[115] plot.spline*                                             
[116] plot.stepfun                                             
[117] plot.stl*                                                
[118] plot.summary.crqs*                                       
[119] plot.summary.formula.response*                           
[120] plot.summary.formula.reverse*                            
[121] plot.summary.rqs*                                        
[122] plot.summary.rqss*                                       
[123] plot.summaryM*                                           
[124] plot.summaryP*                                           
[125] plot.summaryS*                                           
[126] plot.survfit*                                            
[127] plot.table*                                              
[128] plot.table.rq*                                           
[129] plot.testSlopes*                                         
[130] plot.times*                                              
[131] plot.transcan*                                           
[132] plot.trellis*                                            
[133] plot.ts                                                  
[134] plot.tskernel*                                           
[135] plot.TukeyHSD*                                           
[136] plot.varclus*                                            
[137] plot.Variogram*                                          
[138] plot.xyVector*                                           
[139] plot.zoo*                                                
see '?methods' for accessing help and source code

in our data, do customers who buy more online buy less in stores? We start by plotting online sales against in-store sales:

plot(cust.df$store.spend, cust.df$online.spend,
     main="Customers as of June 2014",
     xlab="Prior 12 months in-store sales ($)",
     ylab="Prior 12 months online sales ($)",
     cex=0.7)

Above: The resulting plot in Fig. 4.2 is typical of the skewed distributions that are common in behavioral data such as sales or transaction counts; most customers purchase rarely so the data is dense near zero. The resulting plot has a lot of points along the axes; we use the cex option, which scales down the plotted points to 0.7 of their default size so that we can see the points a bit more clearly. The plot shows that there are a large number of customers who didn’t buy anything on one of the two channels (the points along the axes), along with a smaller number of customers who purchase fairly large amounts on one of the channels.

Because of the skewedness of the data we use a histogram in hopes of better clairty.

hist(cust.df$store.spend,
  breaks=(0:ceiling(max(cust.df$store.spend)/10))*10,
  main="Customers as of June 2014",
  xlab="Prior 12 months online sales ($)",
  ylab="Count of customers")

The histogram in Fig. 4.3 shows clearly that a large number of customers bought nothing in the online store (about 400 out of 1,000). The distribution of sales among those who do buy has a mode around $20 and a long right-hand tail with a few customers whose 12-month spending was high. Such distributions are typical of spending and transaction counts in customer data.

4.2.2 Color-Coding Points on a Scatterplot

Another question is whether the propensity to buy online versus in store is related to our email efforts (as reflected by whether or not a customer has an email address on file).

We can add the email dimension to the plot in Fig. 4.2 by coloring in the points for customers whose email address is known to us. To do this, we use plot() arguments that allow us to draw different colors (col=) and symbols for the points (pch=). Each argument takes a vector that specifies the option—the color or symbol—that you want for each individual point. Thus, if we provide a vector of colors of the same length as the vectors of x and y values, col= will use the corresponding colors for each point. Constructing such vectors can be tricky, so we will build them up slowly.

To begin, we first declare vectors for the color and point types that we want to use:

my.col <- c("black", "green3")
my.pch <- c(1, 19) # R’s symbols for solid and open circles (see ?points)
my.col[as.numeric(head(cust.df$email))]
[1] "green3" "green3" "green3" "green3" "black"  "green3"

Now that we have a vector of colors, we can pass it as the col option in plot() to get a plot where customers with emails on file are plotted in green and customers without email addresses on file are plotted in black. We use a similar strategy for setting the point styles using the pch option, such that customers without email addresses have open circles instead of solid.

plot(cust.df$store.spend, cust.df$online.spend,
 cex=0.7,
 col=my.col[cust.df$email], pch=my.pch[cust.df$email],
 main="Customers as of June 2014",
 xlab="Prior 12 months in-store sales ($)",
 ylab="Prior 12 months online sales ($)")
legend(x="topright", legend=paste("email on file:", levels(cust.df$email)), col=my.col, pch=my.pch)

When we created Fig. 4.1 earlier, we used an option col=“blue” and it turned all of the points blue. This is because if the vector you pass for col is shorter than the length of x and y, then R recycles the values. Thus, if your col vector has one element, all the points will be that single color. Similarly, if you were to pass thevectorc(“black”, “green3”),thenplotwouldsimplymakealternating points black or green, which might not be what you want. Usually what you’ll want is to create a vector that exactly matches the length of your data by starting with a shorter vector as we did here, and then indexing it with such that you extract a value for each one of your data points. That can be difficult to get right in practice, so we encourage you to experiment with these examples until you understand how it works.

4.2.3 Adding a Legend to a Plot

Above

4.2.4 Plotting on a Log Scale

With raw values as plotted in the left panel of Fig. 4.4, it is still difficult to see whether there is a different relationship between in-store and online purchases for those with and without emails on file, because of the heavy skew in sales fig- ures. A common solution for such scatterplots with skewed data is to plot the data on a logarithmic scale. This is easy to do with the log= argument of plot(): set log=“x” to plot the x-axis on the log scale, log=“y” for the y-axis, or log=“xy” for both axes.

Since both values are skewed we use log=“xy” to apply log to both variables

plot(cust.df$store.spend + 1, cust.df$online.spend + 1,
 log="xy", cex=0.7,
 col=my.col[cust.df$email], pch=my.pch[cust.df$email],
 main="Customers as of June 2014",
 xlab="Prior 12 months in-store sales ($)",
 ylab="Prior 12 months online sales ($)" )
 legend(x="topright", legend=paste("email on file:", levels(cust.df$email)),
 col=my.col, pch=my.pch)

Thus, there is no evidence here to suggest that online sales have cannibalized in-store sales (a formal test of that would be complex, but the present data do not argue for such an effect in any obvious way)

We also see in Fig. 4.4 that customers with no email address on file show slightly lower online sales than those with addresses; there are somewhat more black circles in the lower half of the plot than the upper half. If we have been sending email promotions to customers, then this suggests that the promotions might be working. An experiment to confirm that hypothesis could be an appropriate next step.

4.3 Combining Plots in a Single Graphics Object

Sometimes we want to visualize several relationships at once. For instance, suppose we wish to examine whether customers who live closer to stores spend more in store, and whether those who live further away spend more online. Those involve different spending variables and thus need separate plots. If we plot several such things individually, we end up with many individual charts. Luckily, R can produce a single graphic that consists of multiple plots. You do this by telling R that you want multiple plots in a single graphical object with the par(mfrow=…) command; then simply plot each one with plot() as usual.

par(mfrow=c(2, 2))
plot(cust.df$distance.to.store, cust.df$store.spend, main="store")
plot(cust.df$distance.to.store, cust.df$online.spend, main="online")
plot(cust.df$distance.to.store, cust.df$store.spend+1, log="xy",
     main="store, log")
plot(cust.df$distance.to.store, cust.df$online.spend+1, log="xy",
     main="online, log")

Above: the graphical parameter mfrow to c(2, 2), which instructs R to create a single graphic comprising a two-by-two arrangement of plots, which begins on the first row and moves from left to right.

Above: We see a possible negative relationship between spend and distance to store

4.4 scatterplot matrices

4.4.1 pairs()

In our customer data, we have a number of variables that might be associated with each other; age, distance.to.store, and email all might be related to on- line and offline transactions and to spending. When you have several variables such as these, it is good practice to examine scatterplots between all pairs of variables before moving on to more complex analyses.


pairs(formula = ~ age + credit.score + email +
  distance.to.store + online.visits + online.trans +
  online.spend + store.trans + store.spend,
  data=cust.df)

#same as

pairs(cust.df[ , c(2:10)]) #output not repeated; same as above

formula = ∼ age + credit.score + email + distance.to.store + online.visits + online.trans + online.spend + store.trans + store.spend, data=cust.df)

4.4.2 scatterplotMatrix()

the car package adds several features to pairs

library(car)
scatterplotMatrix(formula = ~ age + credit.score + email + distance.to.store + online.visits + online.trans + online.spend + store.trans + store.spend, data = cust.df, diagonal="histogram")
Warning in smoother(x, y, col = col[2], log.x = FALSE, log.y = FALSE, spread = spread,  :
  could not fit smooth
Warning in smoother(x, y, col = col[2], log.x = FALSE, log.y = FALSE, spread = spread,  :
  could not fit smooth
Warning in smoother(x, y, col = col[2], log.x = FALSE, log.y = FALSE, spread = spread,  :
  could not fit smooth
Warning in smoother(x, y, col = col[2], log.x = FALSE, log.y = FALSE, spread = spread,  :
  could not fit smooth
Warning in smoother(x, y, col = col[2], log.x = FALSE, log.y = FALSE, spread = spread,  :
  could not fit smooth
Warning in smoother(x, y, col = col[2], log.x = FALSE, log.y = FALSE, spread = spread,  :
  could not fit smooth
Warning in smoother(x, y, col = col[2], log.x = FALSE, log.y = FALSE, spread = spread,  :
  could not fit smooth
Warning in smoother(x, y, col = col[2], log.x = FALSE, log.y = FALSE, spread = spread,  :
  could not fit smooth

A limitation of Figs. 4.6 and 4.7 concerns the display of the email variable. email is a binary factor with values yes and no, and a scatterplot is not ideal to vi- sualize a discrete variable. For such variables, the gpairs, or Generalized Pair Plots, package [41] provides a function called gpairs() that produces a scat- terplot matrix that includes better visualizations for both discrete and continuous variables. For example, if we want to look more closely at the relationship between email and online.visits, online.trans and online.spend, we can use gpairs() as follows:

 library(gpairs)
Error in library(gpairs) : there is no package called ‘gpairs’

gpairs() produces scatterplots for pairs of continuous variables. However, for the factor email, gpairs includes a boxplot that compares the distribution of continuous variables for those who do and do not have email addresses in the data. A boxplot shows that the distributions of visits, transactions, and spending have longer tails among customers who have email addresses on file than those who don’t. We discuss boxplots in depth in Chap. 5, which focuses on comparisons between groups.

because it selects individual plots to fit the data types, gpairs() is useful for mar- keting data sets that include continuous and discrete variables. Note that gpairs() relies on the data types in R to determine how to construct its plots; if we had stored

4.5 Correlation Coefficients

assess the relationship between each pair with a single number. One measure of the relationship between two variables is the covari- ance, which can be computed for any two variables using the cov function:

cov(cust.df$age, cust.df$credit.score)
[1] 63.23443

However, it is difficult to interpret the magnitude of covariance because the scale depends on the variables involved. Covariance will be different if the variables are measured in cents versus dollars or in inches versus centimeters. So, it is helpful to scale the covariance by the standard deviation for each variable, which results in a standardized, rescaled correlation coefficient known as the Pearson product-moment correlation coefficient, often abbreviated as the symbol r.

cor(cust.df$age, cust.df$credit.score)
[1] 0.2545045

r is identical to rescale the covariance by the join std. dev (but more conv)

cov(cust.df$age, cust.df$credit.score) / (sd(cust.df$age)*sd(cust.df$credit.score))
[1] 0.2545045

What value of r signifies an important correlation between two variables in mar- keting? In engineering and physical sciences, physical measurements may demon- strate extremely high correlations; for instance, r between the lengths and weights of pieces of steel rod might be 0.9, 0.95, or even 0.999, depending on the unifor- mity of the rods and the precision of measurement. However, in social sciences such as marketing, we are concerned with human behavior, which is less consistent and more difficult to measure. This results in lower correlations, but they are still important.

We often use Cohen’s Rules of Thumb, which come out of the psychology tradi- tion [27]. Cohen proposed that for correlations between variables describing peo- ple, r = 0.1 should be considered a small or weak association, r = 0.3 might be considered to be medium in strength, and r = 0.5 or higher could be considered to be large or strong. Cohen’s interpretation of a large effect was that such an asso- ciation would be easily noticed by casual observers. A small effect would require careful measurement to detect yet might be important to our understanding and to statistical models.

Importantly, interpretation of r according to Cohen’s rules of thumb depends on the assumption that the variables are normally distributed (also known as Gaussian) or are approximately so

4.5.1 Correlation Tests

Is it statistically significant?

cor.test(cust.df$age, cust.df$credit.score)

    Pearson's product-moment correlation

data:  cust.df$age and cust.df$credit.score
t = 8.3138, df = 998, p-value = 3.008e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.1955974 0.3115816
sample estimates:
      cor 
0.2545045 

Above: This tells us that r = 0.25 and the 95 % confidence interval is r = 0.196 − 0.312. Because the confidence interval for r does not include 0 (and thus has p-value of p < 0.05), the association is statistically significant. Such a correlation, showing a medium-sized effect and statistical significance, probably should not be ignored in subsequent analyses.

4.5.2 Correlation Matrices

for more than two vars you can compute the corrs between all pairs x,y

corrmat[corrmat > .20]
 [1] 1.0000000 0.2545045 0.2545045 1.0000000 1.0000000 1.0000000 0.9881555 0.9802214 0.9881555 1.0000000 0.9940306 0.9802214 0.9940306 1.0000000
[15] 1.0000000 0.8815039 0.8815039 1.0000000
library(corrplot)    # for correlation plot, install if needed
library(gplots)      # color interpolation, install if needed
corrplot.mixed(corr=cor(cust.df[ , c(2, 3, 5:10)], use="complete.obs"),
               upper="ellipse", tl.pos="lt",
               col = colorpanel(50, "red", "gray60", "blue4"))

numeric values of r are shown in the lower triangle of the matrix. The upper triangle displays ellipses (because we used the argument upper=“ellipse”). These ellipses are tighter, progressively closer to being lines, for larger values of r, and are rounder, more like circles for r near zero. They are also shaded blue for positive direction, and red for negative (and show corresponding positive or negative

This makes it easy to find the larger correlations in the data: age is positively cor- related with credit.score; distance.to.store is negatively correlated with store.trans and store.spend; online.visits, online.trans, and online.spend are all strongly correlated with one another, as are store. trans and store.spend. In the survey items, sat.service is positively cor- related with sat.selection.

This makes it easy to find the larger correlations in the data: age is positively cor- related with credit.score; distance.to.store is negatively correlated with store.trans and store.spend; online.visits, online.trans, and online.spend are all strongly correlated with one another, as are store. trans and store.spend. In the survey items, sat.service is positively cor- related with sat.selection.

4.5.3 Transforming Variables before Computing Correlations

Notice we are comparing x x&2 to show the lack of “linear” correlation though we know they are related

Above: Many relationships in marketing data are nonlinear. The number of trips a customer makes to a store may be inversely related to distance from the store.

cor(cust.df$distance.to.store, cust.df$store.spend)
LS0tCnRpdGxlOiAiUiBmb3IgTWFya2V0aW5nIFJlc2VhcmNoIEFuYWx5dGljcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKI0NoYXB0ZXIgMTogQW4gT3ZlcnZpZXcgb2YgdGhlIFIgTGFuZ3VhZ2UKYGBge3IgQ2hhcHRlciAxIFBhY2thZ2VzfQoKbGlicmFyeShsYXZhYW4pCmxpYnJhcnkoc2VtUGxvdCkKbGlicmFyeShjb3JycGxvdCkKbGlicmFyeShtdWx0Y29tcCkKCmBgYAoKIyMgTG9hZCBUZXN0IERhdGEKYGBge3IgVGVzdCBTY29yZSBEYXRhfQpzYXREYXRhIDwtIHJlYWQuY3N2KCdzYXQuY3N2JykKc2F0RGF0YSRTZWdtZW50IDwtIGZhY3RvcihzYXREYXRhJFNlZ21lbnQpCmhlYWQoc2F0RGF0YSkKCnN1bW1hcnkoc2F0RGF0YSkKCmBgYApgYGB7ciBDb3JycGxvdCB9Cgpjb3JycGxvdC5taXhlZChjb3Ioc2F0RGF0YVssIC0zXSkpCgpgYGAKCgojIyMgRG9lcyBwcm9kdWN0IHNhdGlzZmFjdGlvbiBkaWZmZXIgYnkgc2VnbWVudD8KYGBge3J9CgphZ2dyZWdhdGUoaVByb2RTQVQgfiBTZWdtZW50LCBzYXREYXRhLCBtZWFuKQoKYGBgCgoKIyMjIEFyZSB0aGUgZGlmZmVyZW5jZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50PyAKIyMjIyBTZWdtZW50IDQgaGFzIHRoZSBoaWdoZXN0IGxldmVsIG9mIHNhdGlzZmFjdGlvbiwgYnV0IGFyZSB0aGUgZGlmZmVyZW5jZXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudD8gV2UgcGVyZm9ybSBhIG9uZSB3YXkgYW5hbHlzaXMgb2YgdmFyaWFuY2UgKEFOT1ZBKSBhbmQgc2VlIHRoYXQgc2F0aXNmYWN0aW9uIGRpZmZlcnMgc2lnbmlmaWNhbnRseSBieSBzZWdtZW50OgoKYGBge3IgT25lIFd5IEFOT1ZBfQoKc2F0LmFub3ZhIDwtIGFvdihpUHJvZFNBVCB+IC0xICsgU2VnbWVudCwgc2F0RGF0YSkKc3VtbWFyeShzYXQuYW5vdmEpCgpgYGAKCiMjIyBXZSBwbG90IHRoZSBBTk9WQSBtb2RlbCB0byB2aXN1YWxpemUgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIG1lYW4gcHJvZHVjdCBzYXQtIGlzZmFjdGlvbiBieSBzZWdtZW50OgoKYGBge3IgVmlzdWFsaXNlIEFOT1ZBfQoKICBwYXIobWFyPWMoNCw4LDQsMikpCiAgcGxvdChnbGh0KHNhdC5hbm92YSkpCgpgYGAKCiMjIyBTdHJ1Y3R1YWwgRXF1YXRpb24gTW9kZWwgdG8gdGhlIFNhdGlzZmFjdGlvbiBEYXRhCiMjIyMjIFdlIHByb3Bvc2UgdGhhdCB0aGUgU0FUIGxhdGVudCB2YXJpYWJsZSBpcyBtYW5pZmVzdGVkIGluIHRoZSB0d28gc2F0aXNmYWN0aW9uIGl0ZW1zLCB3aGlsZSBSRUMgaXMgbWFuaWZlc3RlZCBpbiB0aGUgdHdvIGxpa2VsaWhvb2QtdG8tcmVjb21tZW5kIGl0ZW1zLiBBcyBtYXJrZXRlcnMsIHdlIGV4cGVjdCBhbmQgaG9wZSB0aGF0IHRoZSBsYXRlbnQgbGlrZWxpaG9vZC10by1yZWNvbW1lbmQgdmFyaWFibGUgKFJFQykgd291bGQgYmUgYWZmZWN0ZWQgYnkgdGhlIGxhdGVudCBzYXRpc2ZhY3Rpb24gKFNBVCkuCgpgYGB7ciBmaXQgYSBzdHJ1Y3R1cmFsIGVxdWF0aW9uIG1vZGVsIHRvIHRoZSBzYXRpc2ZhY3Rpb24gZGF0YS59CgpzYXRNb2RlbCA8LSAiU0FUID1+IGlQcm9kU0FUICsgaVNhbGVzU0FUCiAgICAgICAgICAgIFJFQyA9fiBpUHJvZFJFQyArIGlTYWxlc1JFQwogICAgICAgICAgICBSRUN+IFNBVCIKCmBgYAoKIyMjIyMgLSBUaGlzIG1vZGVsIG1pZ2h0IGJlIHBhcmFwaHJhc2VkIGFzIOKAnExhdGVudCBTQVRpc2ZhY3Rpb24gaXMgb2JzZXJ2ZWQgYXMgaXRlbXMgaVByb2RTQVQgYW5kIGlTYWxlc1NBVC4gTGF0ZW50IGxpa2VsaWhvb2QgdG8gUkVDb21tZW5kIGlzIG9ic2VydmVkIGFzIGl0ZW1zIGlQcm9kUkVDIGFuZCBpU2FsZXNSRUMuIFJFQ29tbWVuZGF0aW9uIHZhcmllcyB3aXRoIFNBVGlzZmFjdGlvbuKAnS4KCmBgYHtyfQoKc2F0LmZpdCA8LSBjZmEoc2F0TW9kZWwsIGRhdGE9c2F0RGF0YSkKc3VtbWFyeShzYXQuZml0LCBmaXQubT1UUlVFKQoKYGBgCgojIyBXZSB2aXN1YWxpemUgdGhlIHN0cnVjdHVyYWwgbW9kZWwgdXNpbmcgdGhlIHNlbVBsb3QgcGFja2FnZToKIyMjIyBUaGUgbW9kZWwgY29udmVyZ2VkIGFuZCByZXBvcnRlZCBtYW55IHN0YXRpc3RpY3MgdGhhdCB3ZSBvbWl0IGFib3ZlLCBidXQgd2Ugbm90ZSB0aGF0IHRoZSBtb2RlbCBmaXRzIHRoZSBkYXRhIHdlbGwgd2l0aCBhIENvbXBhcmF0aXZlIEZpdCBJbmRleCBuZWFyIDEuMAoKYGBge3IgV2UgdmlzdWFsaXplIHRoZSBzdHJ1Y3R1cmFsIG1vZGVsIHVzaW5nIHRoZSBzZW1QbG90IHBhY2thZ2U6fQoKc2VtUGF0aHMoc2F0LmZpdCwgd2hhdD0iZXN0IiwKICAgICAgICAgIHJlc2lkdWFscz1GQUxTRSwgaW50ZXJjZXB0cz1GQUxTRSwgbkNoYXJOb2Rlcz05KQoKYGBgCiMjIyMgVGhpcyBwcm9kdWNlcyB0aGUgY2hhcnQgc2hvd24gaW4gRmlnLiAyLjQuIEVhY2ggcHJvcG9zZWQgbGF0ZW50IHZhcmlhYmxlIGlzIGhpZ2hseSBsb2FkZWQgb24gaXRzIG1hbmlmZXN0ZWQgKG9ic2VydmVkKSBzdXJ2ZXkgaXRlbXMuIFdpdGggYW4gZXN0aW1hdGVkIGNvZWZmaWNpZW50IG9mIDAuNzYsIGN1c3RvbWVyc+KAmSBsYXRlbnQgc2F0aXNmYWN0aW9uIGlzIHNob3duIHRvIGhhdmUgYSBzdHJvbmcgYXNzb2NpYXRpb24gd2l0aCB0aGVpciBsaWtlbGlob29kIHRvIHJlY29tbWVuZC4KCgoKIyMgMi40LjMgTW9yZSBvbiBWZWN0b3JzIGFuZCBJbmRleGluZwoKYGBge3IgTW9yZSBvbiBWZWN0b3JzIGFuZCBJbmRleGluZ30KCnhTZXEgPC0gMTooNCoyKQp4U2VxCnhTZXEgPC0gMTo0KjIKeFNlcQoKCiMgU2VxdWVuY2VzIGFyZSB1c2VmdWwgZm9yIGluZGV4aW5nIGFuZCB5b3UgY2FuIHVzZSBzZXF1ZW5jZXMgaW5zaWRlIFsgXToKCjE6MzAwCiAgCgoKYGBgCgojIyAyLjQuNSBNaXNzaW5nIGFuZCBJbnRlcmVzdGluZyBWYWx1ZXMKCmBgYHtyIE1pc3NpbmcgYW5kIEludGVyZXN0aW5nIFZhbHVlc30KCm15LnRlc3Quc2NvcmVzIDwtIGMoOTEsIE5BLCBOQSkKbWVhbihteS50ZXN0LnNjb3JlcykKbWF4KG15LnRlc3Quc2NvcmVzKQoKCm1lYW4obXkudGVzdC5zY29yZXMsIG5hLnJtID0gVFJVRSkKbWF4KG15LnRlc3Quc2NvcmVzLCBuYS5ybSA9IFRSVUUpCgoKCiMgQSBzZWNvbmQgYXBwcm9hY2ggaXMgdG8gcmVtb3ZlIE5BIHZhbHVlcyBleHBsaWNpdGx5IGJlZm9yZSBjYWxjdWxhdGluZyBvbiB0aGVtIG9yIGFzc2lnbmluZyB0aGVtIGVsc2V3aGVyZS4gVGhpcyBtYXkgYmUgZG9uZSBtb3N0IGVhc2lseSB3aXRoIHRoZSBjb21tYW5kIG5hLm9taXQoKToKCm1lYW4obmEub21pdChteS50ZXN0LnNjb3JlcykpCgoKIyBBIHRoaXJkIGFuZCBtb3JlIGN1bWJlcnNvbWUgYWx0ZXJuYXRpdmUgaXMgdG8gdGVzdCBmb3IgTkEgdXNpbmcgdGhlIGlzLm5hKCkgZnVuY3Rpb24sIGFuZCB0aGVuIGluZGV4IGRhdGEgZm9yIHRoZSB2YWx1ZXMgdGhhdCBhcmUgbm90IE5BIGJ5IGFkZGluZyB0aGUgISAo4oCcbm904oCdKSBvcGVyYXRvcjoKCmlzLm5hKG15LnRlc3Quc2NvcmVzKQpteS50ZXN0LnNjb3Jlc1shaXMubmEobXkudGVzdC5zY29yZXMpXQoKCgojIE9uZSB0aGluZyBuZXZlciB0byBkbyBpbiBSIGlzIHRvIHVzZSBhbiBhY3R1YWwgbnVtZXJpYyB2YWx1ZSBzdWNoIGFzIOKIkjk5OSB0byBpbi0gZGljYXRlIG1pc3NpbmcgZGF0YS4gVGhhdCB3aWxsIGNhdXNlIGhlYWRhY2hlcyBhdCBiZXN0IGFuZCB3cm9uZyBhbnN3ZXJzIGF0IHdvcnN0LiBJbnN0ZWFkLCBhcyBzb29uIGFzIHlvdSBsb2FkIHN1Y2ggZGF0YSBpbnRvIFIsIHJlcGxhY2UgdGhvc2UgdmFsdWVzIHdpdGggTkEgdXNpbmcgaW5kaWNlczoKCm15LnRlc3Quc2NvcmVzIDwtIGMoOTEsIC05OTksIC05OTkpCm1lYW4obXkudGVzdC5zY29yZXMpCgpteS50ZXN0LnNjb3Jlc1tteS50ZXN0LnNjb3JlcyA8IC05MDBdIDwtIE5BCm1lYW4obXkudGVzdC5zY29yZXMsIG5hLnJtPVRSVUUpCgpsb2coYygtMSwwLDEpKQoKCiMgV2UgZ2V0IGEgd2FybmluZyBiZWNhdXNlIGxvZygpIGlzIHVuZGVmaW5lZCBmb3IgbmVnYXRpdmUgbnVtYmVycyBhbmQgbG9nKC0xKSBnaXZlcyBhIHZhbHVlIG9mIE5hTi4gTm90ZSBhbHNvIHRoYXQgbG9nKDApID0g4oiS4oieICgtSW5mKS4KCgoKYGBgCgojIyAyLjQuNyBMaXN0cwoKYGBge3IgTGlzdHMgYW5kIGxhcHBseX0KCgp4TnVtICA8LSBjKDEsIDMuMTQxNTksIDUsIDcpCnhMb2cgIDwtIGMoVFJVRSwgRkFMU0UsIFRSVUUsIFRSVUUpCnhDaGFyIDwtIGMoImZvbyIsICJiYXIiLCAiYm9vIiwgImZhciIpCnhNaXggIDwtIGMoMSwgVFJVRSwgMywgIkhlbGxvLCB3b3JsZCEiKQoKeExpc3QgPC0gbGlzdCh4TnVtLCB4Q2hhcikKeExpc3QKc3RyKHhOdW0pCnN1bW1hcnkoeExpc3RbWzFdXSkKCgpgYGAKCiMjIyMgSXQgaXMgb2Z0ZW4gbW9yZSBjb252ZW5pZW50IHRvIHJ1biBzdWNoIGEgY29tbWFuZCBvbiBhbGwgbWVtYmVycyBvZiB0aGUgbGlzdCBhdCBvbmNlLiBXZSBjYW4gZG8gdGhhdCB3aXRoIHRoZSBsYXBwbHkoKSBvciDigJxsaXN0IGFwcGx54oCdIGNvbW1hbmQuCgojIyMjIFdpdGggbGFwcGx5KCkgd2UgbXVzdCBwYXkgc3BlY2lhbCBhdHRlbnRpb24gdG8gdGhlIGFyZ3VtZW50IG9yZGVyOiBsYXBwbHkgKE9CSkVDVCxGVU5DVElPTikuV2UgdXNlIGxhcHBseSgpIHRvIHByb2R1Y2UgYSBzdW1tYXJ5KCkgZm9yIGVhY2ggbWVtYmVyIG9mIHRoZSBsaXN0OgpgYGB7ciBsYXBwbHl9CgpsYXBwbHkoeExpc3QsIHN1bW1hcnkpCgoKYGBgCgojIyMjIFVzaW5nIGxhcHBseSgpIHRvIGl0ZXJhdGUgaW4gdGhpcyB3YXkgc2F2ZXMgYSBsb3Qgb2Ygd29yaywgZXNwZWNpYWxseSB3aXRoIGxpc3RzIHRoYXQgbWF5IGNvbXByaXNlIGRvemVucyBvciBodW5kcmVkcyBvZiBvYmplY3RzLiBJdCBkZW1vbnN0cmF0ZXMgdGhhdCBsaXN0cyBoYXZlIHR3byBhZHZhbnRhZ2VzOiB0aGV5IGtlZXAgZGF0YSBpbiBvbmUgcGxhY2UgcmVnYXJkbGVzcyBvZiBjb25zdGl0dWVudCB0eXBlcywgYW5kIHRoZXkgbWFrZSBpdCBwb3NzaWJsZSB0byBhcHBseSBvcGVyYXRpb25zIGF1dG9tYXRpY2FsbHkgdG8gZGl2ZXJzZSBwYXJ0cyBvZiB0aGF0IGRhdGEuCgoKCiMjIDIuNSBEYXRhIEZyYW1lcwoKIyMjIyBEYXRhIGZyYW1lcyBhcmUgdGhlIHdvcmtob3JzZSBvYmplY3RzIGluIFIsIHVzZWQgdG8gaG9sZCBkYXRhIHNldHMgYW5kIHRvIHByb3ZpZGUgZGF0YSB0byBzdGF0aXN0aWNhbCBmdW5jdGlvbnMgYW5kIG1vZGVscy4gQSBkYXRhIGZyYW1l4oCZcyBnZW5lcmFsIHN0cnVjdHVyZSB3aWxsIGJlIGZhLSBtaWxpYXIgdG8gYW55IGFuYWx5c3Q6IGl0IGlzIGEgcmVjdGFuZ3VsYXIgb2JqZWN0IGNvbXByaXNlZCBvZiBjb2x1bW5zIG9mIHZhcnlpbmcgZGF0YSB0eXBlcyAob2Z0ZW4gcmVmZXJyZWQgdG8gYXMg4oCcdmFyaWFibGVz4oCdKSBhbmQgcm93cyB0aGF0IGVhY2ggaGFzIGEgdmFsdWUgKG9yIG1pc3NpbmcgdmFsdWUsIE5BKSBpbiBlYWNoIGNvbHVtbiAo4oCcb2JzZXJ2YXRpb25z4oCdKS4KYGBge3IgRGF0YSBGcmFtZXN9Cgp4LmRmIDwtIGRhdGEuZnJhbWUoeE51bSwgeExvZywgeENoYXIpCnguZGYKCiMgW1JPVywgQ09MVU1OXW5vdGF0aW9uOgp4LmRmWzIsMV0KeC5kZlsxLDNdCmBgYAoKIyMjIyBieSBkZWZhdWx0IFIgY29udmVydHMgY2hhcmFjdGVyIGRhdGEgaW4gZGF0YSBmcmFtZXMgdG8gbm9taW5hbCBmYWN0b3JzLgoKYGBge3J9CiMgCiMgTm9taW5hbCAKIyBPcmRpbmFsIC0gbGVzcy9ncmVhdGVyCiMgSW50ZXJ2YWwgCiMgUmF0aW8gLSB0cnVlIDAKCmBgYAoKQ29udmVydGluZyBjaGFyYWN0ZXIgc3RyaW5ncyB0byBmYWN0b3JzIGlzIGEgZ29vZCB0aGluZyBmb3IgZGF0YSB0aGF0IHlvdSBtaWdodCB1c2UgaW4gYSBzdGF0aXN0aWNhbCBtb2RlbCBiZWNhdXNlIGl0IHRlbGxzIFIgdG8gaGFuZGxlIGl0IGFwcHJvcHJpYXRlbHkgaW4gdGhlIG1vZGVsLCBidXQgaXTigJlzIGluY29udmVuaWVudCB3aGVuIHRoZSBkYXRhIHJlYWxseSBpcyBzaW1wbGUgdGV4dCBzdWNoIGFzIGFuIGFkZHJlc3Mgb3IgY29tLSBtZW50cyBvbiBhIHN1cnZleS4gWW91IGNhbiBwcmV2ZW50IHRoZSBjb252ZXJzaW9uIHRvIGZhY3RvcnMgYnkgYWRkaW5nIGFuIG9wdGlvbiB0byBkYXRhLmZyYW1lKCkgdGhhdCBzZXRzIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0U6CgoKYGBge3IgU2tpcCBzdHJpbmcgYXMgZmFjdG9yIH0KeC5kZiA8LSBkYXRhLmZyYW1lKHhOdW0sIHhMb2csIHhDaGFyLCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKQp4LmRmCnguZGZbMSwzXQoKIyBhbGwgb2JzCnguZGZbMiwgXQojYWxsIGNvbHVtbnMKeC5kZlssMiBdCgp4LmRmWyAsM10gICMgYWxsIG9mIGNvbHVtbiAzCgojIEluZGV4IGRhdGEgZnJhbWVzIGJ5IHVzaW5nIHZlY3RvcnMgb3IgcmFuZ2VzIGZvciB0aGUgZWxlbWVudHMgeW91IHdhbnQuIFVzZSBuZWdhLSB0aXZlIGluZGljZXMgdG8gb21pdCBlbGVtZW50czoKeC5kZlsyOjMsIF0KCgoKcHJpbnQoeC5kZlsgLDE6Ml0pICMgdHdvIGNvbHVtbnMKCnguZGZbLTMsIF0gIyBvbWl0IHRoZSB0aGlyZCBvYnNlcnZhdGlvbgoKeC5kZlssIC0yXSAgIyBvbWl0IHRoZSBzZWNvbmQgY29sdW1uCgpgYGAKCgojIyMgTGV04oCZcyBjcmVhdGUgYSBuZXcgZGF0YSBzZXQgdGhhdCBpcyBtb3JlIHJlcHJlc2VudGF0aXZlIG9mIGRhdGEgaW4gbWFya2V0aW5nIHJlc2VhcmNoLiBXZeKAmWxsIGNsZWFuIHVwIG91ciB3b3Jrc3BhY2UgYW5kIHRoZW4gY3JlYXRlIG5ldyBkYXRhOgoKYGBge3J9CgpybShsaXN0PWxzKCkpICAgICAjIGNhdXRpb24sIGRlbGV0ZXMgYWxsIG9iamVjdHMhIFNlZSBleHBsYW5hdGlvbiBiZWxvdwpzdG9yZS5udW0gPC0gZmFjdG9yKGMoMywgMTQsIDIxLCAzMiwgNTQpKSAjIHN0b3JlIGlkCnN0b3JlLnJldiA8LSBjKDU0MywgNjU0LCAzNDUsIDY3OCwgMjM0KSAgICMgc3RvcmUgcmV2ZW51ZSwgJDEwMDAKc3RvcmUudmlzaXRzIDwtIGMoNDUsIDc4LCAzMiwgNTYsIDM0KSAgICAgIyB2aXNpdHMsIDEwMDBzCnN0b3JlLm1hbmFnZXIgPC0gYygiQW5uaWUiLCAiQmVydCIsICJDYXJsYSIsICJEYXZlIiwgIkVsbGEiKQooc3RvcmUuZGYgPC0gZGF0YS5mcmFtZShzdG9yZS5udW0sIHN0b3JlLnJldiwgc3RvcmUudmlzaXRzLCBzdG9yZS5tYW5hZ2VyLCBzdHJpbmdzQXNGYWN0b3JzPUYpKSAjIEYgPSBGQUxTRQoKIyBQZWFyc29uJ3Mgcgpjb3Ioc3RvcmUuZGYkc3RvcmUucmV2LCBzdG9yZS5kZiRzdG9yZS52aXNpdHMpCnN1bW1hcnkoc3RvcmUuZGYpCgpgYGAKCgojIyAyLjYgTG9hZGluZyBhbmQgU2F2aW5nIERhdGEKCmBgYHtyIGJhY2tpbmcgdXAgUiBvYmplY3RzfQoKc2F2ZShzdG9yZS5kZiwgZmlsZT0ic3RvcmUtZGYtYmFja3VwLlJEYXRhIikKcm0oc3RvcmUuZGYpCiMgbWVhbihzdG9yZS5kZiRzdG9yZS5yZXYpCiAKbG9hZCgic3RvcmUtZGYtYmFja3VwLlJEYXRhIikKbWVhbihzdG9yZS5kZiRzdG9yZS5yZXYpCgoKIyBzYXZlKCkgY2FuIGFsc28gdGFrZSBhIGdyb3VwIG9mIG9iamVjdHMgYXMgYW4gYXJndW1lbnQ7IGp1c3QgcmVwbGFjZSB0aGUgc2luZ2xlIG9iamVjdCBuYW1lIHdpdGggbGlzdD1jKCkgYW5kIGZpbGwgaW4gYygpIHdpdGggYSBjaGFyYWN0ZXIgdmVjdG9yLiBGb3IgaW4tIHN0YW5jZToKc2F2ZShsaXN0PWMoInN0b3JlLmRmIiwic3RvcmUudmlzaXRzIiksIGZpbGU9InN0b3JlLWRmLWJhY2t1cC5SRGF0YSIpCmBgYAoKCiMjIDIuNyBXcml0aW5nIFlvdXIgT3duIEZ1bmN0aW9ucwoKIyMjIyBNYW55IGFuYWx5c2VzIGluIFIgYXJlIHJlcGV0aXRpdmU6IGNvbXB1dGUgc3RhdGlzdGljcyBhY3Jvc3Mgc2xpY2VzIG9mIGRhdGEgc3VjaCBhcyBkaWZmZXJlbnQgc2FsZXMgcmVnaW9ucywgcHJvZHVjZSBhbmFseXNlcyBmcm9tIG5ldyBkYXRhIHNldHMgc3VjaCBhcyBzdWNjZXNzaXZlIGNhbC0gZW5kYXIgcXVhcnRlcnMsIGFuZCBzbyBmb3J0aC4gUiBwcm92aWRlcyBmdW5jdGlvbnMgdG8gbGV0IHlvdSB3cml0ZSBhIHNldCBvZiBjb21tYW5kcyBvbmNlIGFuZCByZXVzZSBpdCB3aXRoIG5ldyBkYXRhLgoKYGBge3Igd3JpdGluZyBmdW5jdGlvbnMgfQoKc2UgPC0gZnVuY3Rpb24oeCkgeyBzZCh4KSAvIHNxcnQobGVuZ3RoKHgpKSB9CnNlKHN0b3JlLmRmJHN0b3JlLnZpc2l0cykKCmBgYAojIyMjIEEgZnVuY3Rpb27igJlzIHJlc3VsdHMgY2FuIGFsc28gYmUgYXNzaWduZWQgdG8gb3RoZXIgdmFyaWFibGVzIG9yIHVzZWQgaW4gYWRkaXRpb25hbCBmdW5jdGlvbnMuIEZvciBleGFtcGxlLCB3ZSBtaWdodCBjb21wdXRlIHRoZSB1cHBlci1ib3VuZCA5NSAlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgYXMgdGhlIG1lYW4gKyAxLjk2IHN0YW5kYXJkIGVycm9yOgpgYGB7ciBjb25maWRlbmNlIGludGVydmFsfQoKbWVhbihzdG9yZS5kZiRzdG9yZS52aXNpdHMpICsgMS45NiAqIHNlKHN0b3JlLmRmJHN0b3JlLnZpc2l0cykKCmBgYAojIyMjIFRoaXMgdGVsbHMgdXMgdGhhdCwgaWYgdGhlIHByZXNlbnQgZGF0YSBhcmUgYSBnb29kIHJhbmRvbSBzYW1wbGUgZnJvbSBhIGxhcmdlciBzZXQsIHdlIGNvdWxkIGV4cGVjdCB0aGUgbWVhbiBvZiBvdGhlciBzdWNoIHNhbXBsZXMgdG8gYmUgNjUuNTEgb3IgbGVzcyBpbiA5Ny41ICUgb2YgdGhlIHNhbXBsZXMgKDk3LjUgJSBiZWNhdXNlIHRoZSA5NSAlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgaXMgc3ltbWV0cmljIGFyb3VuZCA1MCAlLCBleHRlbmRpbmcgZnJvbSAyLjUgdG8gOTcuNSAlKS4gSW4gb3RoZXIgd29yZHMsIHdlIGNhbiBiZSBoaWdobHkgY29uZmlkZW50IGZyb20gdGhlc2UgZGF0YSB0aGF0IHRoZSBtZWFuIG51bWJlciBvZiBzdG9yZSB2aXNpdHMgaXMgbGVzcyB0aGFuIDY1LjUyLgoKIyMjIyBBc2NoZW1hdGljIGZvciBhIG5ldyBmdW5jdGlvbiBpczogRlVOQ1RJT05OQU1FIDwtIGZ1bmN0aW9uKElOUFVUUykgRVhQUiAuIEluIG1vc3QgY2FzZXMsIEVYUFIgaXMgYSBzZXQgb2YgbXVsdGlwbGUgbGluZXMgdGhhdCBvcGVyYXRlIG9uIHRoZSBpbnB1dHMuIFdoZW4gdGhlcmUgYXJlIG11bHRpcGxlIGxpbmVzLCB0aGV5IG11c3QgYmUgZW5jbG9zZWQgd2l0aCBicmFjZXMgeyBhbmQgfS4gQnkgZGVmYXVsdCwgdGhlIHJldHVybiB2YWx1ZSBvZiB0aGUgZnVuY3Rpb24gaXMgdGhlIG91dHB1dCBvZiB0aGUgbGFzdCBjb21tYW5kIGluIHRoZSBmdW5jdGlvbiBkZWNsYXJhdGlvbi4KCgojIyMjIDQgY29udmVudGlvbnMgdG8gd3JpdGluZyBmdW5jdGlvbnM6CuKAoiBQdXQgYnJhY2VzIGFyb3VuZCB0aGUgYm9keSB1c2luZyB7IGFuZCB9LCBldmVuIGlmIGl04oCZcyBqdXN0IGEgb25lIGxpbmUgZnVuY3Rpb24gCuKAoiBDcmVhdGUgdGVtcG9yYXJ5IHZhbHVlcyB0byBob2xkIHJlc3VsdHMgYWxvbmcgdGhlIHdheSBpbnNpZGUgdGhlIGZ1bmN0aW9uCuKAoiBDb21tZW50IHRoZSBmdW5jdGlvbiBwcm9mdXNlbHkK4oCiIFVzZSB0aGUga2V5d29yZCByZXR1cm4oKSB0byBzaG93IHRoZSBleHBsaWNpdCB2YWx1ZSByZXR1cm5lZCBieSB0aGUgZnVuY3Rpb24uCgoKYGBge3Igc2UgZnVuY3Rpb259CgpzZSA8LSBmdW5jdGlvbih4KSB7CiAgIyBjb21wdXRlcyBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbWVhbgogIHRtcC5zZCA8LSBzZCh4KSAgICAgICMgc3RhbmRhcmQgZGV2aWF0aW9uCiAgdG1wLk4gIDwtIGxlbmd0aCh4KSAgIyBzYW1wbGUgc2l6ZQogIHRtcC5zZSA8LSB0bXAuc2QgLyBzcXJ0KHRtcC5OKSAgICMgc3RkIGVycm9yIG9mIHRoZSBtZWFuCiAgcmV0dXJuKHRtcC5zZSkKfQoKYGBgCgojIyAyLjcuMSBMYW5ndWFnZSBTdHJ1Y3R1cmVzCgojIyMjIElmIHlvdSBwcm9ncmFtIGluIGEgbGFuZ3VhZ2Ugc3VjaCBhcyBDIG9yIEphdmEsIHRoZSBjb250cm9sIHN0cnVjdHVyZXMgaW4gUiB3aWxsIGJlIGZhbWlsaWFyLiBVc2luZyBURVNUIHRvIGluZGljYXRlIGEgQm9vbGVhbiB2YWx1ZSAob3IgdmFsdWUgY29lcmNpYmxlIHRvIEJvb2xlYW4pIGFuZCBFWFBSIGZvciBhbnkgbGFuZ3VhZ2UgZXhwcmVzc2lvbuKAlHdoaWNoIG1heSBpbmNsdWRlIGEgYmxvY2sgb2YgZXhwcmVzc2lvbnMgaW5zaWRlIHsgYW5kIH3igJRSIHByb3ZpZGVzOgoKYGBge3J9CiMgRVhQUiA9IGV4cHJlc3Npb24oKQojIGlmIChURVNUKSBFWFBSIFtlbHNlIEVYUFIuYl0gICMgZG8gRVhQUiBpZiBURVNUIGlzIHRydWUsIGVsc2UgRVhQUi5iCiMgd2hpbGUgKFRFU1QpIEVYUFIgCiMgCiMgZm9yIChOQU1FIGluIFZFQ1RPUikgRVhQUiAjIGl0ZXJhdGUgZXhwciBmb3IgdmFsdWVzIG9mIG5hbWUgZnJvbSB2ZWN0b3IKIyAKIyBzd2l0Y2goSU5ERVgsIExJU1QpICMgSU5ERVh0aCBzdGF0ZW1lbnQgb3IgbWF0Y2hpbmcgYXJndW1lbnQgbmFtZSBmcm9tIExJU1QKCiMgdHlwZSA8LSAndHJpbW1lZCcKIyBzd2l0Y2godHlwZSwKIyAgIG1lYW4gPSBtZWFuKHgpLAojICAgbWVkaWFuID0gbWVkaWFuKHgpLAojICAgdHJpbW1lZCA9IG1lYW4oeCwgdHJpbSA9IC4xKSkKCgojIHJlcGVhdCBFWFBSICMgcmVwZWF0cyBmb3JldmVyIHVudGlsIGJyZWFrOyBub3QgcmVjb21tZW5kZWQKYGBgCiMjIyMgVmVjdG9yaXNlZCB2ZXJzaW9uIG9mIGlmIHN0YXRlbWVudCBjYWxsZWQgaWZlbHNlKCksIGNoZWNrcyBpZiBlbGVtZW50IG9uIGEgY29uZGl0aW9uIGFuZCBjb252ZXJ0cyBpdCB0byBhIHRydWUgb3IgZmFsc2UgdGhlbiB0cmFuc2Zvcm1zIGl0IGJhc2VkIG9uIHlvdXIgaW5wdXQuCgoKIyMjIyAKYGBge3IgdmVjdG9yaXNlZCB2ZXJzaW9uIG9mIGlmfQoKP3N3aXRjaAoKbGlicmFyeShkZXZ0b29scykKeCA8LSAtMjoyCmxvZyh4KQoKaWZlbHNlKHggPiAwLCB4LCBOQSkKCmxvZyhpZmVsc2UoeCA+IDAsIHgsIE5BKSkKCmBgYAoKIyMjIDIuNy4yIEFub255bW91cyBGdW5jdGlvbnMKIyMjIyBhbHNvIGtub3duIGFzIGEgbGFtYmRhIGV4cHJlc3Npb24KIyMjIyBhcHBseSB3b3JrcyBmb3Igbm9uLWxpc3RzIGxpa2UgdmVjdG9ycy9kYXRhZnJhbWVzCgpgYGB7ciBhbm9ueW1vdXMgZnVuY3Rpb25zIC0gbGFtYmRhIH0KCm15LmRhdGEgPC0gbWF0cml4KHJ1bmlmKDEwMCksIG5jb2wgPSA1KSAjIDEwMCByYW5kIG51bXMgaW4gNSBjb2xzCm1lZGlhbihteS5kYXRhWywxXSkvMgphcHBseShteS5kYXRhLCAyLCBtZWRpYW4pIC8gMgoKYGBgCgoKIyMgMi44IENsZWFuIFVwCgpgYGB7ciB9CgpscygpICMgdG8gc2VlIHdoYXQgeW91IGhhdmUgaW4gbWVtb3J5CnJtKCkgI3RvIHJlbW92ZSB0aGUgb2JqZWN0CgpybShsaXN0PWxzKCkpCgpgYGAKCgojIyAyLjEwIEtleSBQb2ludHMKCk1vc3Qgb2YgdGhlIHByZXNlbnQgY2hhcHRlciBpcyBmb3VuZGF0aW9uYWwgdG8gUiwgeWV0IHRoZXJlIGFyZSBhIGZldyBlc3BlY2lhbGx5IGltcG9ydGFudCBwb2ludHM6CgotIOKAoiBGb3Igd29yayB0aGF0IHlvdSB3YW50IHRvIHByZXNlcnZlIG9yIGVkaXQsIHVzZSBhIHRleHQgZWRpdG9yIGFuZCBydW4gY29tbWFuZHMgZnJvbSB0aGVyZSAoU2VjdC4gMi4zKS4KCi0g4oCiIENyZWF0ZSB2ZWN0b3JzIHVzaW5nIGMoKSBmb3IgZW51bWVyYXRlZCB2YWx1ZXMsIHNlcSgpIGZvciBzZXF1ZW5jZXMsIGFuZCByZXAoKSBmb3IgcmVwZWF0ZWQgdmFsdWVzIChTZWN0cy4gMi40LjEgYW5kIDIuNC4zKS4KCi0g4oCiIFVzZSB0aGUgY29uc3RhbnQgTkEgZm9yIG1pc3NpbmcgdmFsdWVzLCBub3QgYW4gYXJiaXRyYXJ5IHZhbHVlIHN1Y2ggYXMg4oiSOTk5IChTZWN0LiAyLjQuNSkuCgotIOKAoiBJbiBSLCBkYXRhIHNldHMgYXJlIG1vc3QgY29tbW9ubHkgZGF0YS5mcmFtZSBvYmplY3RzIGNyZWF0ZWQgd2l0aCBhIGNvbW1hbmQgc3VjaCBhcyBteS5kZiA8LSBkYXRhLmZyYW1lKHZlY3RvcjEsIHZlY3RvcjIsIC4uLikgKFNlY3QuIDIuNSkgb3IgYnkgcmVhZGluZyBhIGRhdGEgZmlsZS4KCi0g4oCiIFZlY3RvcnMgYW5kIGRhdGEgZnJhbWVzIGFyZSBtb3N0IG9mdGVuIGluZGV4ZWQgd2l0aCBzcGVjaWZpYyBudW1iZXJzICh4WzFdKSwgcmFuZ2VzICh4WzI6NF0pLCBuZWdhdGl2ZSBpbmRpY2VzICh4Wy0zXSkgdG8gb21pdCBkYXRhLCBhbmQgYnkgYm9vbGVhbiBzZWxlY3Rpb24gKHhbeD4zXSkgKFNlY3RzLiAyLjUgYW5kIDIuNC4zKS4KCi0g4oCiIERhdGEgZnJhbWVzIGFyZSBpbmRleGVkIGJ5W1JPVyxDT0xVTU5dLCB3aGVyZSBhIGJsYW5rIHZhbHVlIG1lYW5zIOKAnGFsbCBvZiB0aGF0IGRpbWVuc2lvbuKAnSBzdWNoIGFzIG15LmRmWzIsXSBmb3Igcm93MiwgYWxsIGNvbHVtbnMoU2VjdC4yLjUpLgoKLSDigKIgWW91IGNhbiBhbHNvIGluZGV4IGEgZGF0YSBmcmFtZSB3aXRoICQgYW5kIGEgY29sdW1uIG5hbWUsIHN1Y2ggYXMgbXkuZGYkaWQgKFNlY3QuIDIuNSkuCgotIOKAoiBSZWFkIGFuZCB3cml0ZSBkYXRhIGluIENTViBmb3JtYXQgd2l0aCByZWFkLmNzdigpIGFuZCB3cml0ZS5jc3YoKSAoU2VjdC4gMi42LjIpLgoKLSDigKIgRnVuY3Rpb25zIGFyZSBzdHJhaWdodGZvcndhcmQgdG8gd3JpdGUgYW5kIGV4dGVuZCBS4oCZcyBjYXBhYmlsaXRpZXMuIFdoZW4geW91IHdyaXRlIGEgZnVuY3Rpb24sIG9yZ2FuaXplIHRoZSBjb2RlIHdlbGwgYW5kIGNvbW1lbnQgaXQgcHJvZnVzZWx5IChTZWN0LiAyLjcpLgoKLSDigKIgQ2xlYW51cHlvdXJ3b3Jrc3BhY2VyZWd1bGFybHl0b2F2b2lkY2x1dHRlcmFuZGJ1Z3Nmcm9tb2Jzb2xldGV2YXJpLSBhYmxlcyAoU2VjdC4gMi44KS4KCgoKIyBQYXJ0IElJIC0gRnVuZGFtZW50YWxzIG9mIERhdGEgQW5hbHlzaXMKCiMgMy4gRGVzY3JpYmUgRGF0YQoKIyMgMy4xIFNpbXVsYXRpbmcgRGF0YQoKIyMjIDMuMS4xCgojIyMjIE91ciBmaXJzdCBkYXRhIHNldCByZXByZXNlbnRzIG9ic2VydmF0aW9ucyBvZiB0b3RhbCBzYWxlcyBieSB3ZWVrIGZvciB0d28gcHJvZHVjdHMgYXQgYSBjaGFpbiBvZiBzdG9yZXMuIFdlIGJlZ2luIGJ5IGNyZWF0aW5nIGEgZGF0YSBzdHJ1Y3R1cmUgdGhhdCB3aWxsIGhvbGQgdGhlIGRhdGEsIGEgc2ltdWxhdGlvbiBvZiBzYWxlcyBmb3IgdGhlIHR3byBwcm9kdWN0cyBpbiAyMCBzdG9yZXMgb3ZlciAyIHllYXJzLCB3aXRoIHByaWNlIGFuZCBwcm8tIG1vdGlvbiBzdGF0dXMuIFdlIHJlbW92ZSBtb3N0IG9mIHRoZSBSIG91dHB1dCBoZXJlIHRvIGZvY3VzIG9uIHRoZSBpbnB1dCBjb21tYW5kcy4KCmBgYHtyIFN0b3JlIERhdGE6IFNldHRpbmcgdGhlIFN0cnVjdHVyZX0KCmsuc3RvcmVzIDwtIDIwICMgMjAgc3RvcmVzLCB1c2luZyAiay4iIGZvciAiY29uc3RhbnQiCmsud2Vla3MgPC0gMTA0ICAgICMgMiB5ZWFycyBvZiBkYXRhIGVhY2gKIyBjcmVhdGVkIGEgZGF0YSBmcmFtZSBvZiBpbml0aWFsbHkgbWlzc2luZyB2YWx1ZXMgdG8gaG9sZCB0aGUgZGF0YQoKc3RvcmUuZGYgPC0gZGF0YS5mcmFtZShtYXRyaXgoTkEsIG5jb2w9MTAsIG5yb3c9ay5zdG9yZXMqay53ZWVrcykpCgpuYW1lcyhzdG9yZS5kZikgPC0gYygic3RvcmVOdW0iLCAiWWVhciIsICJXZWVrIiwgInAxc2FsZXMiLCAicDJzYWxlcyIsInAxcHJpY2UiLCAicDJwcmljZSIsICJwMXByb20iLCAicDJwcm9tIiwiY291bnRyeSIpCgpkaW0oc3RvcmUuZGYpCgoKIyB3ZSBjcmVhdGUgdHdvIHZlY3RvcnMgdGhhdCB3aWxsIHJlcHJlc2VudCB0aGUgc3RvcmUgbnVtYmVyIGFuZCBjb3VudHJ5IGZvciBlYWNoIG9ic2VydmF0aW9uCnN0b3JlLm51bSA8LSAxMDE6KDEwMCtrLnN0b3JlcykKCihzdG9yZS5jdHkgPC0gYyhyZXAoIlVTIiwgMyksIHJlcCgiREUiLCA1KSwgcmVwKCJHQiIsIDMpLCByZXAoIkJSIiwgMiksIHJlcCgiSlAiLCA0KSwgcmVwKCJBVSIsIDEpLCByZXAoIkNOIiwgMikpKQoKbGVuZ3RoKHN0b3JlLmN0eSkgCgojIG5vdyB3ZSByZXBsYWNlIHRoZSBhcHByb3BpYXRlIGNvbHVtbnMgaW4gdGhlIGRhdGEgZnJhbWUgd2l0aCB0aG9zZSB2YWx1ZXMgdXNpbmcgcmVwKCkgdG8gZXhwYW5kIHRoZSB2ZWN0b3JzIHRvIG1hdGNoIHRoZSBudW1iZXIgb2Ygc3RvcmVzIGFuZCB3ZWVrcwoKc3RvcmUuZGYkc3RvcmVOdW0gPC0gcmVwKHN0b3JlLm51bSwgZWFjaCA9IGsud2Vla3MpCnN0b3JlLmRmJGNvdW50cnkgPC0gcmVwKHN0b3JlLmN0eSwgZWFjaCA9IGsud2Vla3MpCnJtKHN0b3JlLm51bSwgc3RvcmUuY3R5KSAgICAjIGNsZWFuIHVwCgpzdHIoc3RvcmUuZGYpCgojIGNoZWNrIHRoZSB0eXBlcywgbm90aWNlIHRoZSBjaHIgdHlwZSB2ZWN0b3JzLCBpZiB0aGV5IGFyZSBkaXNjcmV0ZSB2YWx1ZXMgbGlrZSAnY291bnRyeScgdGhlbiB0aGV5IHNob3VsZCBiZSAiZmFjdG9yZWQiIHVzaW5nIGZhY3RvcigpLCBzdG9yZU51bSBpcyBhIGxhYmVsIHNvIHdlIGNhbiB0cmVhdCBpdCBhcyBhIGZhY3RvciBhcyBpdCBpc24ndCBhICJudW1iZXIiIGluIHRoZSBudW1lcmljYWwgc2Vuc2Uobm90IGZvciBjYWxjdWxhdGlvbikgb2YgdGhlIHdvcmQuCgoKI25leHQgd2UgZG8gdGhlIHNhbWUgdG8gdGhlIHdlZWsgYW5kIHllYXIKKHN0b3JlLmRmJFdlZWsgPC0gcmVwKDE6NTIsIHRpbWVzID0gay5zdG9yZXMqMikpCiN0cnkgdGhlIGlubmVyIHBhcnRzIG9mIHRoZSBuZXh0IGxpbmUgdG8gZmlndXJlIG91dCBob3cgd2UgdXNlIHJlcAooc3RvcmUuZGYkWWVhciAgPC0gcmVwKHJlcCgxOjIsIGVhY2g9ay53ZWVrcy8yKSwgdGltZXM9ay5zdG9yZXMpKQoKc3RvcmUuZGYkc3RvcmVOdW0gPC0gZmFjdG9yKHN0b3JlLmRmJHN0b3JlTnVtKQogc3RvcmUuZGYkY291bnRyeSAgPC0gZmFjdG9yKHN0b3JlLmRmJGNvdW50cnkpCgogCiAKc3RyKHN0b3JlLmRmKQoKI2luc3BjdCBkYXRhIGZyYW1lIGF0IGZpcnN0IGFuZCBsYXN0IHJvd3MKaGVhZChzdG9yZS5kZiwgMTIwKSAgIyAxMjAgcm93cyBpcyBlbm91Z2ggdG8gY2hlY2sgMiBzdG9yZXM7IG5vdCBzaG93bgp0YWlsKHN0b3JlLmRmLCAxMjApICAjIG1ha2Ugc3VyZSBlbmQgbG9va3MgT0sgdG9vOyBub3Qgc2hvd24KCmBgYAoKIyMgMy4xLjIgU3RvcmUgRGF0YTogU2ltdWxhdGluZyBEYXRhIFBvaW50cwoKIyMjIyBXZSBjb21wbGV0ZSBzdG9yZS5kZiB3aXRoIHJhbmRvbSBkYXRhIGZvciBzdG9yZS1ieS13ZWVrIG9ic2VydmF0aW9ucyBvZiB0aGUgc2FsZXMsIHByaWNlLCBhbmQgcHJvbW90aW9uYWwgc3RhdHVzIG9mIDIgcHJvZHVjdHMuCgojIyMjICFCZWZvcmUgc2ltdWxhdGluZyByYW5kb20gZGF0YSwgaXQgaXMgaW1wb3J0YW50IHRvIHNldCB0aGUgcmFuZG9tIG51bWJlciBnZW5lcmF0aW9uIHNlZWQgdG8gbWFrZSB0aGUgcHJvY2VzcyByZXBsaWNhYmxlLgoKCmBgYHtyfQoKP3NldC5zZWVkCnNldC5zZWVkKDk4MjUwKSAgIyBhIGZhdm9yaXRlIFVTIHBvc3RhbCBjb2RlCgojbm93IGRyYXcgdGhlIHJhbmRvbSBkYXRhOyBlYWNoIHJvdyBpcyBvbmUgd2VlayBvZiBvbmUgeWVhciBmb3Igb25lIHN0b3JlCiMgd2Ugc2V0IHRoZSBzdGF0dXMgb2Ygd2hldGhlciBlYWNoIHByb2R1Y3Qgd2FzIHByb21vdGVkICh2YWx1ZSAxKSBieSBkcmF3aW5nIGZyb20gdGhlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiB0aGF0IGNvdW50cyB0aGUgbnVtYmVyIG9mIOKAnGhlYWRz4oCdIGluIGEgY29sbGVjdGlvbiBvZiBjb2luIHRvc3NlcyAod2hlcmUgdGhlIGNvaW4gY2FuIGhhdmUgYW55IHByb3BvcnRpb24gb2YgaGVhZHMsIG5vdCBqdXN0IDUwICUpLgoKIyB3ZSBzZXQgdGhlIHN0YXR1cyBvZiB3aGV0aGVyIGVhY2ggcHJvZHVjdCB3YXMgcHJvbW90ZWQgKHZhbHVlIDEpIGJ5IGRyYXdpbmcgZnJvbSB0aGUgYmlub21pYWwgZGlzdHJpYnV0aW9uIHRoYXQgY291bnRzIHRoZSBudW1iZXIgb2Yg4oCcaGVhZHPigJ0gaW4gYSBjb2xsZWN0aW9uIG9mIGNvaW4gdG9zc2VzICh3aGVyZSB0aGUgY29pbiBjYW4gaGF2ZSBhbnkgcHJvcG9ydGlvbiBvZiBoZWFkcywgbm90IGp1c3QgNTAgJSkuCgpuPW5yb3coc3RvcmUuZGYpCm4KCmBgYAoKVG8gZGV0YWlsIHRoYXQgcHJvY2Vzczogd2UgdXNlIHRoZSByYmlub20obiwgc2l6ZSwgcCkgKGRlY29kZWQgYXMg4oCccmFuZG9tIGJpbm9taWFs4oCdKSBmdW5jdGlvbiB0byBkcmF3IGZyb20gdGhlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbi4gRm9yIGV2ZXJ5IHJvdyBvZiB0aGUgc3RvcmUgZGF0YSwgYXMgbm90ZWQgYnkgbj1ucm93KHN0b3JlLmRmKSwgd2UgZHJhdyBmcm9tIGEgZGlzdHJpYnV0aW9uIHJlcHJlc2VudGluZyB0aGUgbnVtYmVyIG9mIGhlYWRzIGluIGEgc2luZ2xlIGNvaW4gdG9zcyAoc2l6ZT0xKSB3aXRoIGEgY29pbiB0aGF0IGhhcyBwcm9iYWJpbGl0eSBwPTAuMSBmb3IgcHJvZHVjdCAxIGFuZCBwPTAuMTUgZm9yIHByb2R1Y3QgMi4gSW4gb3RoZXIgd29yZHMsIHdlIGFyYml0cmFyaWx5IGFzc2lnbiBhIDEwICUgbGlrZWxpaG9vZCBvZiBwcm9tb3Rpb24gZm9yIHByb2R1Y3QgMSwgYW5kIDE1JSBsaWtlbGlob29kIGZvciBwcm9kdWN0IDIgYW5kIHRoZW4gcmFuZG9tbHkgZGV0ZXJtaW5lIHdoaWNoIHdlZWtzIGhhdmUgcHJvbW90aW9ucy4KCmBgYHtyfQpzdG9yZS5kZiRwMXByb20gPC0gcmJpbm9tKG49bnJvdyhzdG9yZS5kZiksIHNpemU9MSwgcD0wLjEpICAjIDEwJSBwcm9tb3RlZAoKc3RvcmUuZGYkcDJwcm9tIDwtIHJiaW5vbShuPW5yb3coc3RvcmUuZGYpLCBzaXplPTEsIHA9MC4xNSkgIyAxNSUgcHJvbW90ZWQKCmhlYWQoc3RvcmUuZGYpICAjIGhvdyBkb2VzIGl0IGxvb2sgc28gZmFyPyAobm90IHNob3duKQoKI05leHQgd2Ugc2V0IGEgcHJpY2UgZm9yIGVhY2ggcHJvZHVjdCBpbiBlYWNoIHJvdyBvZiB0aGUgZGF0YS4gV2Ugc3VwcG9zZSB0aGF0IGVhY2ggcHJvZHVjdCBpcyBzb2xkIGF0IG9uZSBvZiBmaXZlIGRpc3RpbmN0IHByaWNlIHBvaW50cyByYW5naW5nIGZyb20gJDIuMTkgdG8gJDMuMTkgb3Zlci0gYWxsLiBXZSByYW5kb21seSBkcmF3IGEgcHJpY2UgZm9yIGVhY2ggd2VlayBieSBkZWZpbmluZyBhIHZlY3RvciB3aXRoIHRoZSBmaXZlIHByaWNlIHBvaW50c2FuZHVzaW5nc2FtcGxlKHgsIHNpemUsIHJlcGxhY2UpdG9kcmF3ZnJvbWl0YXNtYW55dGltZXMgYXMgd2UgaGF2ZSByb3dzIG9mIGRhdGEgKHNpemU9bnJvdyhzdG9yZS5kZikpLiBUaGUgZml2ZSBwcmljZXMgYXJlIHNhbXBsZWQgbWFueSB0aW1lcywgc28gd2Ugc2FtcGxlIHdpdGggcmVwbGFjZW1lbnQgKHJlcGxhY2U9VFJVRSk6CgpzdG9yZS5kZiRwMXByaWNlIDwtIHNhbXBsZSh4PWMoMi4xOSwgMi4yOSwgMi40OSwgMi43OSwgMi45OSksIHNpemU9bnJvdyhzdG9yZS5kZiksIHJlcGxhY2U9VFJVRSkKc3RvcmUuZGYkcDJwcmljZSA8LSBzYW1wbGUoeD1jKDIuMjksIDIuNDksIDIuNTksIDIuOTksIDMuMTkpLAogIHNpemU9bnJvdyhzdG9yZS5kZiksIHJlcGxhY2U9VFJVRSkKaGVhZChzdG9yZS5kZikgICMgbm93IGhvdyBkb2VzIGl0IGxvb2s/CgojIFF1ZXN0aW9uOiBpZiBwcmljZSBvY2N1cnMgYXQgZml2ZSBkaXNjcmV0ZSBsZXZlbHMsIGRvZXMgdGhhdCBtYWtlIGl0IGEgZmFjdG9yIHZhcmlhYmxlPz8/PyBUaGF0IGRlcGVuZHMgb24gdGhlIGFuYWx5dGljIHF1ZXN0aW9uLCBidXQgaW4gZ2VuZXJhbCBwcm9iYWJseSBub3QuIFdlIG9mdGVuIHBlcmZvcm0gbWF0aCBvbiBwcmljZSwgc3VjaCBhcyBzdWJ0cmFjdGluZyBjb3N0IGluIG9yZGVyIHRvIGZpbmQgZ3Jvc3MgbWFyZ2luLCBtdWx0aXBseWluZyBieSB1bml0cyB0byBmaW5kIHRvdGFsIHNhbGVzLCBhbmQgc28gZm9ydGguIFRodXMsIGV2ZW4gdGhvdWdoIGl0IG1heSBoYXZlIG9ubHkgYSBmZXcgdW5pcXVlIHZhbHVlcywgcHJpY2UgaXMgYSBudW1iZXIsIG5vdCBhIGZhY3Rvci4KCgojIE91ciBsYXN0IHN0ZXAgaXMgdG8gc2ltdWxhdGUgdGhlIHNhbGVzIGZpZ3VyZXMgZm9yIGVhY2ggd2Vlay4gV2UgY2FsY3VsYXRlIHNhbGVzIGFzIGEgZnVuY3Rpb24gb2YgdGhlIHJlbGF0aXZlIHByaWNlcyBvZiB0aGUgdHdvIHByb2R1Y3RzIGFsb25nIHdpdGggdGhlIHByb21vdGlvbmFsIHN0YXR1cyBvZiBlYWNoLgoKIyBJdGVtIHNhbGVzIGFyZSBpbiB1bml0IGNvdW50cywgc28gd2UgdXNlIHRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbiB0byBnZW5lcmF0ZSBjb3VudCBkYXRhOiBycG9pcyhuLCBsYW1iZGEpLCB3aGVyZSBuIGlzIHRoZSBudW1iZXIgb2YgZHJhd3MgYW5kIGxhbWJkYSBpcyB0aGUgbWVhbiB2YWx1ZSBvZiB1bml0cyBwZXIgd2Vlay4gV2UgZHJhdyBhIHJhbmRvbSBQb2lzc29uIGNvdW50IGZvciBlYWNoIHJvdyAobnJvdyhzdG9yZS5kZiksIGFuZCBzZXQgdGhlIG1lYW4gc2FsZXMgKGxhbWJkYSkgb2YgUHJvZHVjdCAxIHRvIGJlIGhpZ2hlciB0aGFuIHRoYXQgb2YgUHJvZHVjdCAyOgpgYGAKCgpgYGB7cn0KCiMgc2FsZXMgZGF0YSwgdXNpbmcgcG9pc3NvbiAoY291bnRzKSBkaXN0cmlidXRpb24sIHJwb2lzKCkKP3Jwb2lzCgpvdCA8LSBycG9pcyg1LCBsYW1iZGEgPSAxMCkKcDFwcmljZSA8LSBzYW1wbGUoeD1jKDIuMTksIDIuMjksIDIuNDksIDIuNzksIDIuOTkpLCBzaXplPTUsIHJlcGxhY2U9VFJVRSkKCgp1bml0X3ByaWNlIDwtMjAKdW5pdHNfc29sZCA8LSA1Cgp1bml0X3NfcHJpY2UgPC0gMTUKCnNhbGVzIDwtIHVuaXRfcHJpY2UqdW5pdHNfc29sZApzYWxlcwoKc2FsZXMvdW5pdF9zX3ByaWNlICMgeW91IHdvdWxkIG5lZWQgdG8gc2VsbCB0aGlzIG1hbnkgbW9yZSB1bml0cyBhdCB0aGUgdW5pdF9zX3ByaWNlIHRvIG1hdGNoIHJldmVudWUgaW4gdGhlIGRlZmF1bHQgdW5pdF9wcmljZQoKb3QKcDFwcmljZQp0dDwtb3QqcDFwcmljZQp0dAp0dC9wMXByaWNlCgpvdDEgPC0gcnBvaXMoNSwgbGFtYmRhID0gMTUpCm1lYW4ob3QpCm1lYW4ob3QxKQoKeGEgPC0gYygyLDMsNCw1KQp5YSA8LSBjKDQsNiw4LDEwKQoKeGEKbG9nKHhhKQp5YQpsb2coeWEpCgojIHNhbGVzIGRhdGEsIHVzaW5nIHBvaXNzb24gKGNvdW50cykgZGlzdHJpYnV0aW9uLCBycG9pcygpCnRtcC5zYWxlczEgPC0gcnBvaXMobnJvdyhzdG9yZS5kZiksIGxhbWJkYSA9IDEyMCkKdG1wLnNhbGVzMiA8LSBycG9pcyhucm93KHN0b3JlLmRmKSwgbGFtYmRhID0gMTAwKQoKI05vdyB3ZSBzY2FsZSB0aG9zZSBjb3VudHMgdXAgb3IgZG93biBhY2NvcmRpbmcgdG8gdGhlIHJlbGF0aXZlIHByaWNlcy4gUHJpY2UgZWZmZWN0cyBvZnRlbiBmb2xsb3cgYSBsb2dhcml0aG1pYyBmdW5jdGlvbiByYXRoZXIgdGhhbiBhIGxpbmVhciBmdW5jdGlvbiwgc28gd2UgdXNlIGxvZyhwcmljZSkgaGVyZToKCiMgc2NhbGUgc2FsZXMgYWNjb3JkaW5nIHRvIHRoZSByYXRpbyBvZiBsb2cocHJpY2UpCnRtcC5zYWxlczEgPC0gdG1wLnNhbGVzMSAqIGxvZyhzdG9yZS5kZiRwMnByaWNlKSAvIGxvZyhzdG9yZS5kZiRwMXByaWNlKQoKdG1wLnNhbGVzMiA8LSB0bXAuc2FsZXMyICogbG9nKHN0b3JlLmRmJHAxcHJpY2UpIC8gcHJpbnQobG9nKHN0b3JlLmRmJHAycHJpY2UpKQoKIyBmaW5hbCBzYWxlcyBnZXQgYSAzMCUgb3IgNDAlIGxpZnQgd2hlbiBwcm9tb3RlZApzdG9yZS5kZiRwMXNhbGVzIDwtIGZsb29yKHRtcC5zYWxlczEgKiAoMSArIHN0b3JlLmRmJHAxcHJvbSowLjMpKQpzdG9yZS5kZiRwMnNhbGVzIDwtIGZsb29yKHRtcC5zYWxlczIgKiAoMSArIHN0b3JlLmRmJHAycHJvbSowLjQpKQoKCiMgaGVhZChzdG9yZS5kZikKCmxpYnJhcnkoY2FyKQojIGdpdmVzIHVzIGEgcmFuZG9tIHNldCB0byBpbnNwZWN0CnNvbWUoc3RvcmUuZGYsIDEwKQoKYGBgCgoKCiMjIDMuMiBGdW5jdGlvbnMgdG8gU3VtbWFyaXNlIGEgVmFyaWFibGUKIyMjIyBPYnNlcnZhdGlvbnMgbWF5IGNvbXByaXNlIGVpdGhlciAqZGlzY3JldGUqIG9yICpjb250aW51b3VzKiBkYXRhIHdpdGggbWFueSBwb3NzaWJsZSB2YWx1ZXMuCgpcW3NeezJ9ID0gXGZyYWN7XHN1bSh4IC0gXGJhcnt4fSleMn17biAtIDF9IFxdCgoKCmBgYHtyIH0KaGlzdChzdG9yZS5kZiRwMXByaWNlKQoKcDEudGFibGUgPC0gdGFibGUoc3RvcmUuZGYkcDFwcmljZSkKcDEudGFibGUKcGxvdChwMS50YWJsZSkKCm9iamVjdC5zaXplKHN0b3JlLmRmKQpvYmplY3Quc2l6ZShwMS50YWJsZSkKCgoKCmBgYAotIEFib3ZlCiNZb3UgY2FuIHNlZSBmcm9tIHRoZSByZXN1bHRpbmcgYmFyIHBsb3QgaW4gRmlnLiBBYm92ZSB0aGF0IHRoZSBwcm9kdWN0IHdhcyBvbiBzYWxlIGF0IGVhY2ggcHJpY2UgcG9pbnQgcm91Z2hseSB0aGUgc2FtZSBudW1iZXIgb2YgdGltZXMuIFIgY2hvc2UgYSB0eXBlIG9mIHBsb3Qgc3VpdGFibGUgZm9yIG91ciB0YWJsZSBvYmplY3QsIGJ1dCBpdCBpcyBmYWlybHkgdWdseSBhbmQgdGhlIGxhYmVscyBjb3VsZCBiZSBjbGVhcmVyLiBMYXRlciBpbiB0aGlzIGNoYXB0ZXIgd2Ugc2hvdyBob3cgdG8gbW9kaWZ5IGEgcGxvdCB0byBnZXQgYmV0dGVyIHJlc3VsdHMuCgojIEFuIGFuYWx5c3QgbWlnaHQgd2FudCB0byBrbm93IGhvdyBvZnRlbiBlYWNoIHByb2R1Y3Qgd2FzIHByb21vdGVkIGF0IGVhY2ggcHJpY2UgcG9pbnQuIFRoZSB0YWJsZSgpIGNvbW1hbmQgcHJvZHVjZXMgdHdvLXdheSBjcm9zcyB0YWJzIHdoZW4gYSBzZWNvbmQgdmFyaWFibGUgaXMgaW5jbHVkZWQ6CgpgYGB7ciBDcm9zcyBUYWJzfQojIHBsYWNlcyB2YXIxIGFzIHRoZSByb3cgaW5kZXggYW5kIHZhcjIgYXMgdGhlIGNvbCBpbmRleAojIGFibGUgdG8gc2VlIGhvdyBwcm9tb3Rpb25zIGFuZCBub24tcHJvbW90aW9uIGl0ZW1zIHNlbGwgYWNjb3JkaW5nIHRvIHByb2R1Y3Qgb25lIHByaWNlCnRhYmxlKHN0b3JlLmRmJHAxcHJpY2UsIHN0b3JlLmRmJHAxcHJvbSkKCnBsb3QoKHRhYmxlKHN0b3JlLmRmJHAxcHJpY2UsIHN0b3JlLmRmJHAxcHJvbSkpKQpwbG90KCh0YWJsZShzdG9yZS5kZiRwMnByaWNlLCBzdG9yZS5kZiRwMnByb20pKSkKCiNBdCBlYWNoIHByaWNlIGxldmVsLCBQcm9kdWN0IDEgaXMgb2JzZXJ2ZWQgdG8gaGF2ZSBiZWVuIHByb21vdGVkIGFwcHJveGltYXRlbHkgMTAgJSBvZiB0aGUgdGltZSAoYXMgZXhwZWN0ZWQsIGdpdmVuIGhvdyB3ZSBjcmVhdGVkIHRoZSBkYXRhIGluIFNlY3QuIDMuMS4xKS4gSW4gZmFjdCwgd2UgY2FuIGNvbXB1dGUgdGhlIGV4YWN0IGZyYWN0aW9uIG9mIHRpbWVzIHByb2R1Y3QgMSBpcyBvbiBwcm9tb3Rpb24gYXQgZWFjaCBwcmljZSBwb2ludCwgaWYgd2UgYXNzaWduIHRoZSB0YWJsZSB0byBhIHZhcmlhYmxlIGFuZCB0aGVuIGRpdmlkZSB0aGUgc2Vjb25kIGNvbHVtbiBvZiB0aGUgdGFibGUgYnkgdGhlIHN1bSBvZiB0aGUgZmlyc3QgYW5kIHNlY29uZCBjb2x1bW5zOgoKcDEudGFibGUyIDwtIHRhYmxlKHN0b3JlLmRmJHAxcHJpY2UsIHN0b3JlLmRmJHAxcHJvbSkKcDEudGFibGUyCiMgc3VtIHRoZSB0d28gY29sdW1ucyB0byBnZXQgdGhlIHRvdGFsIHRoZW4gZGl2aWRlIGl0IGJ5IHNlY29uZCBjb2x1bW4gKGZyZXEgb2YgcHJvbW90aW9ucykgdG8gZ2V0IHRoZSBmcmFjdGlvbiBvciByYXRpbyBvZiBwcmljZS9wcm9tCnAxLnRhYmxlMlssIDJdIC8gKHAxLnRhYmxlMlssIDFdICsgcDEudGFibGUyWywgMl0pCgpgYGAKCgoKIyMjIyBUaGUgc2Vjb25kIGNvbW1hbmQgdGFrZXMgdGhlIHNlY29uZCBjb2x1bW4gb2YgdGFibGUgcDEudGFibGXigJR0aGUgY29sdW1uIHdpdGggY291bnRzIG9mIGhvdyBvZnRlbiB0aGUgcHJvZHVjdCBpcyBwcm9tb3RlZOKAlGFuZCBkaXZpZGVzIGJ5IHRoZSB0b3RhbCBjb3VudCB0byBnZXQgdGhlIHByb3BvcnRpb24gb2YgdGltZXMgdGhlIHByb2R1Y3Qgd2FzIHByb21vdGVkIGF0IGVhY2ggcHJpY2UgcG9pbnQuIFIgYXV0by0gbWF0aWNhbGx5IGFwcGxpZXMgbWF0aCBvcGVyYXRvcnMgKyBhbmQgLyBhY3Jvc3MgdGhlIGVudGlyZSBjb2x1bW5zLgoKIyMjIyBCeSBjb21iaW5pbmcgcmVzdWx0cyBpbiB0aGlzIHdheSwgeW91IGNhbiBlYXNpbHkgcHJvZHVjZSBleGFjdGx5IHRoZSByZXN1bHRzIHlvdSB3YW50IGFsb25nIHdpdGggY29kZSB0aGF0IGNhbiByZXBlYXQgdGhlIGFuYWx5c2lzIG9uIGRlbWFuZC4gVGhpcyBpcyB2ZXJ5IGhlbHBmdWwgdG8gbWFya2V0aW5nIGFuYWx5c3RzIHdobyBwcm9kdWNlIHdlZWtseSBvciBtb250aGx5IHJlcG9ydHMgZm9yIHNhbGVzLCB3ZWIgdHJhZmZpYywgYW5kIHRoZSBsaWtlLgoKCgojIyAzLjIuMiBDb250aW51b3VzIFZhcmlhYmxlcwoKIyMjIyBDb3VudHMgYXJlIHVzZWZ1bCB3aGVuIHRoZXJlIGlzIGEgc21hbGwgbnVtYmVyIG9mIGNhdGVnb3JpZXMsIGJ1dCB3aXRoIGNvbnRpbnVvdXMgZGF0YSBpdCBpcyBtb3JlIGhlbHBmdWwgdG8gc3VtbWFyaXNlIHRoZSBkYXRhIGluIHRlcm1zIG9mIGl0cyBkaXN0cmlidXRpb24uCgpgYGB7ciBEaXN0cmlidXRpb24gZnVuY3Rpb25zIHRoYXQgb3BlcmF0ZSBvbiBhIG51bWVyaWMgdmVjdG9yfQoKZGVzY3JpYmVzIDwtIGMoIkV4dHJlbWVzIiwgJ0NlbnRyYWwgVGVuZGVuY3knLCAnRGlzcGVyc2lvbicsICdQb2ludHMnKQpmdW5jdGlvbnMgPC0gYygibWluKHgpLCBtYXgoeCkiLCAnbWVhbih4KSwgbWVkaWFuICh4KScsICd2YXIoeCksIHNkKHgpLCBJUVIoeCksIG1hZCh4KScsICdxdWFudGlsZSh4LCBwcm9icz1jKC4uLikpJykKdmFsdWVzIDwtIGMoIk1pbiBNYXggVmFsdWUiLCAnQXJpdGggTWVhbiAvIE1lZGlhbicsICdWYXJpYW5jZSBhcm91bmQgdGhlIG1lYW4sIFN0YW5kLiBEZXYsIHNxcnQodmFyKHgpKSwgSW50ZXJxdWFydGlsZSByYW5nZSg3NS0yNSUpLCBtZWRpYW4gYWJzb2x1dGUgZGV2aWF0aW9uIChyb2J1c3QgdmFyaWFuY2UgZXN0aW1hdG9yKScsICdQZXJjZW50aWxlcycpCgpkaXN0ZnVuY3MgPC0gZGF0YS5mcmFtZSgKICBkZXNjcmliZXMsIAogIGZ1bmN0aW9ucywKICB2YWx1ZXMKKQp0YWJsZShkaXN0ZnVuY3MpCgpgYGAKCmBgYHtyIERpc3QgZnVuY3N9CgptaW4oc3RvcmUuZGYkcDFzYWxlcykKbWF4KHN0b3JlLmRmJHAyc2FsZXMpCm1lYW4oc3RvcmUuZGYkcDFwcm9tKQptZWRpYW4oc3RvcmUuZGYkcDJzYWxlcykKdmFyKHN0b3JlLmRmJHAxc2FsZXMpCnNkKHN0b3JlLmRmJHAxc2FsZXMpCklRUihzdG9yZS5kZiRwMXNhbGVzKQptYWQoc3RvcmUuZGYkcDFzYWxlcykKcXVhbnRpbGUoc3RvcmUuZGYkcDFzYWxlcywgcHJvYnM9YygwLjI1LCAwLjUsIDAuNzUpKQoKYGBgCgojIyMjIEluIHRoZSBjYXNlIG9mIHF1YW50aWxlKCkgd2UgaGF2ZSBhc2tlZCBmb3IgdGhlIDI1dGgsIDUwdGgsIGFuZCA3NXRoIHBlcmNlbnRpbGVzIHVzaW5nIHRoZSBhcmd1bWVudCBwcm9icz1jKDAuMjUsIDAuNSwgMC43NSksIHdoaWNoIGFyZSBhbHNvIGtub3duIGFzIHRoZSBtZWRpYW4gKDUwdGggcGVyY2VudGlsZSwgc2FtZSBhcyB0aGUgbWVkaWFuKCkgZnVuY3Rpb24pIGFuZCB0aGUgZWRnZXMgb2YgdGhlIGludGVycXVhcnRpbGUgcmFuZ2UsIHRoZSAyNXRoIGFuZCA3NXRoIHBlcmNlbnRpbGVzLgoKIyMjIyBGb3Igc2tld2VkIGFuZCBhc3ltbWV0cmljIGRpc3RyaWJ1dGlvbnMgdGhhdCBhcmUgY29tbW9uIGluIG1hcmtldGluZywgc3VjaCBhcyB1bml0IHNhbGVzIG9yIGhvdXNlaG9sZCBpbmNvbWUsIHRoZSBhcml0aG1ldGljIG1lYW4oKSBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIHNkKCkgbWF5IGJlIG1pc2xlYWRpbmc7IGluIHRob3NlIGNhc2VzLCB0aGUgbWVkaWFuKCkgYW5kIGludGVycXVhcnRpbGUgcmFuZ2UgKElRUigpLCB0aGUgcmFuZ2Ugb2YgdGhlIG1pZGRsZSA1MCAlIG9mIGRhdGEpIGFyZSBvZnRlbiBtb3JlIHVzZWZ1bCB0byBzdW1tYXJpemUgYSBkaXN0cmlidXRpb24uCgpgYGB7cn0KCiNDaGFuZ2UgdGhlIHByb2JzPSBhcmd1bWVudCBpbiBxdWFudGlsZSgpIHRvIGZpbmQgb3RoZXIgcXVhbnRpbGVzOgpxdWFudGlsZShzdG9yZS5kZiRwMXNhbGVzLCBwcm9icz1jKDAuMDUsIDAuOTUpKSAgIyBjZW50cmFsIDkwJSBvZiBkYXRhCgojdXNpbmcgYSBzZXF1ZW5jZSB3aGNpaCBnaXZlcyB1cyAgdGhlIGZvbGxvd2luZwowOjEwLzEwCiMgbm93IHdlIHdpbGwgcGFzcyB0aGlzIHNlcXVlbmNlIGludG8gdGhlIHF1YW50aWxlCnF1YW50aWxlKHN0b3JlLmRmJHAxc2FsZXMsIHByb2JzPTA6MTAvMTApCgpgYGAKCgojIyMjIFN1cHBvc2Ugd2Ugd2FudGVkIGEgc3VtbWFyeSBvZiB0aGUgc2FsZXMgZm9yIHByb2R1Y3QgMSBhbmQgcHJvZHVjdCAyIGJhc2VkIG9uIHRoZWlyIG1lZGlhbiBhbmQgaW50ZXJxdWFydGlsZSByYW5nZS4gV2UgbWlnaHQgYXNzZW1ibGUgdGhlc2Ugc3VtbWFyeSBzdGF0aXN0aWNzIGludG8gYSBkYXRhIGZyYW1lIHRoYXQgaXMgZWFzZXIgdG8gcmVhZCB0aGFuIHRoZSBvbmUtbGluZS1hdC1hLXRpbWUgb3V0cHV0IGFib3ZlLiBXZSBjcmVhdGUgYSBkYXRhIGZyYW1lIHRvIGhvbGQgb3VyIHN1bW1hcnkgc3RhdGlzdGljcyBhbmQgdGhlbiBwb3B1bGF0ZSBpdCB1c2luZyBmdW5jdGlvbnMgZnJvbSBUYWJsZSAzLjEuIFdlIG5hbWUgdGhlIGNvbHVtbnMgYW5kIHJvd3MsIGFuZCBmaWxsIGluIHRoZSBjZWxscyB3aXRoIGZ1bmN0aW9uIHZhbHVlczoKCgpgYGB7ciBzdW1tYXJ5IG9mIGFsZXMgZm9yIHAxIGFuZCBwMiB9CgojIGNyZWF0ZWQgMiBieSAyIGRhdGFmcmFtZQpteXN1bW1hcnkuZGYgPC0gZGF0YS5mcmFtZShtYXRyaXgoTkEsIG5yb3c9MiwgbmNvbD0yKSkKCiMgbGFiZWwgdGhlIGNvbHVtbiBuYW1lcwpuYW1lcyhteXN1bW1hcnkuZGYpIDwtIGMoIk1lZGlhbiBTYWxlcyIsICJJUVIiKQoKIyBsYWJlbCB0aGUgaW5kZXhlcyBvZiB0aGUgb2JzCnJvd25hbWVzKG15c3VtbWFyeS5kZikgPC0gYygiUHJvZHVjdCAxIiwgIlByb2R1Y3QgMiIpCgojIHNlbGVjdCBieSByb3cgaW5kZXggYW5kIHNldCBtZWRpYW4gdG8gLT4KbXlzdW1tYXJ5LmRmWyJQcm9kdWN0IDEiLCAiTWVkaWFuIFNhbGVzIl0gPC0gbWVkaWFuKHN0b3JlLmRmJHAxc2FsZXMpCgojIHNlbGVjdCBieSByb3cgaW5kZXggYW5kIHNldCBtZWRpYW4gdG8gLT4KbXlzdW1tYXJ5LmRmWyJQcm9kdWN0IDIiLCAiTWVkaWFuIFNhbGVzIl0gPC0gbWVkaWFuKHN0b3JlLmRmJHAyc2FsZXMpCgoKbXlzdW1tYXJ5LmRmWyJQcm9kdWN0IDEiLCAiSVFSIl0gPC0gSVFSKHN0b3JlLmRmJHAxc2FsZXMpCgpteXN1bW1hcnkuZGZbIlByb2R1Y3QgMiIsICJJUVIiXSA8LSBJUVIoc3RvcmUuZGYkcDJzYWxlcykKCm15c3VtbWFyeS5kZgoKYGBgCgoKIyMjIyBBYm92ZTogV2l0aCB0aGlzIGN1c3RvbSBzdW1tYXJ5IHdlIGNhbiBlYXNpbHkgc2VlIHRoYXQgbWVkaWFuIHNhbGVzIGFyZSBoaWdoZXIgZm9yIHByb2QtIHVjdCAxICgxMjkgdmVyc3VzIDk2KSBhbmQgdGhhdCB0aGUgdmFyaWF0aW9uIGluIHNhbGVzIG9mIHByb2R1Y3QgMSAodGhlIElRUiBhY3Jvc3Mgb2JzZXJ2YXRpb25zIGJ5IHdlZWspIGlzIGFsc28gaGlnaGVyLgoKIyMgMy4zIFN1bW1hcmlzaW5nIERhdGEgRnJhbWVzCgojIyMjIGl0IGlzIHRlZGlvdXMgdG8gYXBwbHkgdGhlbSBvbmUgYXQgYSB0aW1lIHRvIGNvbHVtbnMgb2YgYSBsYXJnZSBkYXRhIGZyYW1lCgojIyMjIFdlIGRlc2NyaWJlIHRocmVlIGFwcHJvYWNoZXM6IHRoZSBiYXNpYyBzdW1tYXJ5KCkgY29tbWFuZCwgdGhlIGRlc2NyaWJlKCkgY29tbWFuZCBmcm9tIHRoZSBwc3ljaCBwYWNrYWdlLCBhbmQgdGhlIFIgYXBwcm9hY2ggdG8gaXRlcmF0aW5nIG92ZXIgdmFyaWFibGVzIHdpdGggYXBwbHkoKS4KCmBgYHtyIHN1bW1tYXJ5fQpzdW1tYXJ5KHN0b3JlLmRmKQoKIyBzdW1tYXJ5IGZvciBzaW5nbGUgdmVjdG9yCnN1bW1hcnkoc3RvcmUuZGYkWWVhcikKCiMgZGlnaXRzIGZvciBwcmVjaXNpb24Kc3VtbWFyeShzdG9yZS5kZiwgZGlnaXRzPTIpCgojIFIgZ2VuZXJhbGx5IHVzZXMgZGlnaXRzIHRvIG1lYW4gc2lnbmlmaWNhbnQgZGlnaXRzIHJlZ2FyZGxlc3Mgb2YgYWJzb2x1dGUgbWFnbml0dWRlIG9yIHRoZSBkZWNpbWFsIHBvc2l0aW9uLiBUaHVzLCBkaWdpdHM9MyBkb2VzIG5vdCBtZWFuIOKAnHRocmVlIGRlY2ltYWwgcGxhY2Vz4oCdIGJ1dCBpbnN0ZWFkIOKAnHRocmVlIHNpZ25pZmljYW50IHBvc2l0aW9ucy7igJ0gT3V0cHV0IGNvbmZvcm1pbmcgdG8gZGlnaXRzPSBpcyBub3QgZ3VhcmFudGVlZDsgdGhlIGZvcm1hdCBtYXkgYmUgZGlmZmVyZW50IGluIHZhcmlvdXMgY2FzZXMgc3VjaCBhcyByZXBvcnRpbmcgaW50ZWdlciB2YWx1ZXMgYW5kIGZvciBmYWN0b3JzLgoKCgpgYGAKCgojIyMgVXNlIHN1bW1hcnkoKSBhZnRlciB0aGUgZGF0YSBpcyBpbXBvcnRlZCBhbmQgeW91IHdhbnQgdG8gY2hlY2sgdGhlIHF1YWxpdHkKLSBDaGVjayBtaW4gYW5kIG1heCBmb3Igb3V0bGllcnMKLSBDaGVjayBNZWFuIGFuZCBtZWRpYW4gYXJlIHJlYXNvbmFibGUgYW5kIHNpbWlsaWFyIHRvIGVhY2ggb3RoZXIKCgojIyAzLjMuMiBkZXNjcmliZSgpCgotIG4KLSB0aGUgY291bnQgb2Ygb2JzZXJ2YXRpb25zOyAKLSBtZWRpYW4KLSBtZWFuCi0gdHJpbW1lZCBtZWFuICh0aGUgbWVhbiBhZnRlciBkcm9wcGluZyBhIHNtYWxsIHByb3BvcnRpb24gb2YgZXh0cmVtZSB2YWx1ZXMpIAotIHNrZXcgCi0ga3VydG9zaXMgCi0gbWFkCi0gcmFuZ2UKLSBzZQoKdXNlZnVsIHdoZW4gaW50ZXJwcmV0aW5nIGRhdGEgd2l0aCByZWdhcmQgdG8gbm9ybWFsIGRpc3RyaWJ1dGlvbnMuCgoKYGBge3IgcHN5Y2h9CgpsaWJyYXJ5KHBzeWNoKQpkZXNjcmliZShzdG9yZS5kZikKcHJpbnQoIiIpCiMgaWYgeW91IGhhdmUgbm9uLW51bWVyaWMgZmFjdG9yIHZhcmlhYmxlcyB5b3UnbGwgZ2V0IGFuIGVycm9yLCB0byByZXNvbHZlIHRoYXQgcGFzcyBpbiBzcGVjaWZpYyBjb2x1bW5zCmRlc2NyaWJlKHN0b3JlLmRmWyAsIGMoMiwgNDo5KV0pCgoKYGBgCgotIGNvbXBhcmVkIHRyaW1tZWQgbWVhbiB0byBvdmVyYWxsIG1lYW4KLSAqIG5leHQgdG8gZmFjdG9yIHZhcmlhYmxlcwoKCiMjIDMuMy4zIFJlY29tbWVuZGVkIEFwcHJvYWNoIHRvIEluc3BlY3RpbmcgRGF0YQoKLSBpbXBvcnQgZGF0YSB3aXRoIHJlYWQuY3N2KCkKLSBjb252ZXJ0IGl0IGludG8gYSBkYXRhZnJhbWUgKG15LmRhdGEgPC0gZGF0YS5mcmFtZShEQVRBKSBhbmQgc2V0IGNvbHVtbiBuYW1lcyAobmFtZXMobXlkYXRhKSA+LSBjKC4uKSkpCi0gZXhhbWluZSBkaW0oKQotIHVzZSBoZWFkKCkgYW5kIHRhaWwoKSBjaGVjayBmaXJzdCBhbmQgbGFzdCByZXZpZXdzCi0gdXNlIHNvbWUoKSBmcm9tIHRoZSBjYXIgcGFja2FnZSB0byBleGFtaW5lIGEgZmV3IHNldHMgb2YgcmFuZG9tIHJvd3MKLSBjaGVjayBkYXRhZnJhbWUgc3RydWN0dXJlIHN0cigpIHRvIGVuc3VyZSB2YXIgdHlwZXMgYW5kIHZhbHVlcyBhcmUgYXBwcm8uICggY2hhbmdlIHR5cGVzIGVzcGVjaWFsbHkgdG8gZmFjdG9yIHR5cGVzKQotIHJ1biBzdW1tYXJ5KCkgYW5kIGxvb2sgZm9yIHVuZXhwZWN0ZWQgdmFsdWVzKGVzcC4gbWluKCkgbWF4KCkpCi0gbG9hZCB0aGUgcHN5Y2ggbGlicmFyeSBhbmQgZXhhbWluZSBiYXNpYyBkZXNjcmlwdGl2ZXMgd3RpaCBkZXNjcmliZSgpIChyZWNvbmZpcm0gdGhlIG9icyBjb3VudHMgYnkgY2hlY2tpbmcgdGhhdCBuIGlzIHRoZSBzYW1lIGFuZCBjaGVjayB0cmltbWVkIG1lYW4gYW5kIHNrZXcpCgojIyAzLjMuNCBhcHBseSgpKgoKYGBge3J9CmFwcGx5KHg9REFUQSwgTUFSR0lOPU1BUkdJTiwgRlVOPUZVTkNUSU9OKSAKYGBgCgojIyMjIEluIFIgdGhlIHRlcm0gbWFyZ2luIGlzIGEgdHdvLWRpbWVuc2lvbmFsIG1ldGFwaG9yIHRoYXQgZGVub3RlcyB3aGljaCDigJxkaXJlYy0gdGlvbuKAnSB5b3Ugd2FudCB0byBkbyBzb21ldGhpbmc6IGVpdGhlciBhbG9uZyB0aGUgcm93cyAoTUFSR0lOPTEpIG9yIGNvbHVtbnMgKE1BUkdJTj0yKSwgb3IgYm90aCBzaW11bHRhbmVvdXNseSAoTUFSR0lOPWMoMSwgMikpLgoKIyMjIwpgYGB7ciBmaW5kIHRoZSBtZWFuIG9mIGV2ZXJ5IGNvbHVtbiBvZiBzdG9yZS5kZiBleGNlcHQgZm9yIHN0b3JlLmRmJFN0b3JlfQoKbmFtZXMoc3RvcmUuZGZbLDI6OV0pCgphcHBseShzdG9yZS5kZlssMjo5XSwgTUFSR0lOPTIsIEZVTj1tZWFuKQoKY29sTWVhbnMoc3RvcmUuZGZbLDI6OV0pCgpgYGAKCgojIyMjIEFib3ZlOiAgQXMgaXQgaGFwcGVucywgY29sTWVhbnMoKSBkb2VzIHRoZSBzYW1lIHRoaW5nIGFzIHRoZSBjb21tYW5kIGFib3ZlLCBidXQgYXBwbHkgZ2l2ZXMgeW91IHRoZSBmbGV4aWJpbGl0eSB0byBhcHBseSBhbnkgZnVuY3Rpb24geW91IGxpa2UuIElmIHdlIHdhbnQgdGhlIHJvdyBtZWFucyBpbnN0ZWFkLCB3ZSBzaW1wbHkgY2hhbmdlIHRoZSBtYXJnaW4gdG8gMToKCgojIyMjIElmIHdlIHdhbnQgdGhlIHJvdyBtZWFucwoKYGBge3Igcm93IG1lYW5zfQoKYXBwbHkoc3RvcmUuZGZbLDI6OV0sIDEsIG1lYW4pCiMgYnkgYWRkaW5nIDEgYXMgdGhlIHNlY29uZCBwYXJhbWV0ZXIsIHdoaWNoIGlzIG5vdyBtYXJnaW4gKG1hcmdpbiA9IDEpIGl0IHdpbGwgdGFrZSB0aGUgcm93cwoKCmBgYAoKYGBge3Igc3VtKCkgYW5kIHNkKCl9CgojU2ltaWxhcmx5LCB3ZSBtaWdodCBmaW5kIHRoZSBzdW0oKSBvciBzZCgpIGZvciBtdWx0aXBsZSBjb2x1bW5zIHdpdGggbWFyZ2luPTI6CmFwcGx5KHN0b3JlLmRmWywyOjldLCAyLCBzdW0pCmFwcGx5KHN0b3JlLmRmWywyOjldLCAyLCBzZCkKCmBgYAoKIyMjIyB3ZSBhcmUgY2hlY2tpbmcgZGF0YSBhbmQgd2lzaCB0byBrbm93IHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG1lYW4gYW5kIG1lZGlhbiBvZiBlYWNoIHZhcmlhYmxlLCBwZXJoYXBzIHRvIGZsYWcgc2tldyBpbiB0aGUgZGF0YS4KCmBgYHtyIGFub255bW91cyBmdW5jdGlvbiB9CgphcHBseShzdG9yZS5kZlssMjo5XSwgMiwgZnVuY3Rpb24oeCkgeyBtZWFuKHgpIC0gbWVkaWFuKHgpIH0gKQoKCmBgYAoKQXBwbHkgaXMgYSBmdW5jdGlvbmFsIG1ldGhvZCwgcGVvcGxlIHRoYXQgYXJlIHVzZSB0byBwcm9jZWR1cmFsIHByb2dyYW1taW5nICBtaWdodCBlbnZpc2lvbiBzb2x2aW5nIHRoaXMgd2l0aCBhIGZvciBsb29wLiBSIGlzIHByaW1hcmlseSBhIGZ1bmN0aW9uYWwgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UuIE9uZSBtdXN0IHRyeSB0byB0aGluayBpbiB0ZXJtcyBvZiBmdW5jdGlvbnMgdGhhdCBhcmUgYXBwbGllZCBhY3Jvc3MgZGF0YSBhcyB3ZSBkbyBoZXJlLgoKYGBge3J9CgpteXN1bW1hcnkyLmRmIDwtIGRhdGEuZnJhbWUobWF0cml4KE5BLCBucm93PTIsbmNvbD0yKSkKCm5hbWVzKG15c3VtbWFyeTIuZGYpIDwtIGMoIk1lZGlhbiBTYWxlcyIsICJJUVIiKQoKcm93bmFtZXMobXlzdW1tYXJ5Mi5kZikgPC0gbmFtZXMoc3RvcmUuZGYpWzQ6NV0gIyBuYW1lcyBmcm9tIHRoZSBkYXRhIGZyYW1lCgpteXN1bW1hcnkyLmRmCgpteXN1bW1hcnkyLmRmWywgIk1lZGlhbiBTYWxlcyJdIDwtIGFwcGx5KHN0b3JlLmRmWyw0OjVdLCAyLCBtZWRpYW4pCgpteXN1bW1hcnkyLmRmWywgIklRUiJdIDwtIGFwcGx5KHN0b3JlLmRmWywgNDo1XSwgMiwgSVFSKQoKbXlzdW1tYXJ5Mi5kZgoKYGBgCklmIHRoZXJlIHdlcmUgbWFueSBwcm9kdWN0cyBpbnN0ZWFkIG9mIGp1c3QgdHdvLCB0aGUgY29kZSB3b3VsZCBzdGlsbCB3b3JrIGlmIHdlIGNoYW5nZWQgdGhlIG51bWJlciBvZiBhbGxvY2F0ZWQgcm93cywgYW5kIGFwcGx5KCkgd291bGQgcnVuIGF1dG9tYXRpY2FsbHkgYWNyb3NzIGFsbCBvZiB0aGVtLgoKYGBge3J9Cm15c3VtbWFyeTIuZGYgPC0gZGF0YS5mcmFtZShtYXRyaXgoTkEsIG5yb3c9MixuY29sPTQpKQoKbmFtZXMobXlzdW1tYXJ5Mi5kZikgPC0gYygiTWVkaWFuIFNhbGVzIiwgIklRUiIsICJNZWFuIFNhbGVzIiwgImt1cnRvc2kiKQoKcm93bmFtZXMobXlzdW1tYXJ5Mi5kZikgPC0gbmFtZXMoc3RvcmUuZGYpWzQ6NV0gIyBuYW1lcyBmcm9tIHRoZSBkYXRhIGZyYW1lCgpteXN1bW1hcnkyLmRmCgpteXN1bW1hcnkyLmRmWywgIk1lZGlhbiBTYWxlcyJdIDwtIGFwcGx5KHN0b3JlLmRmWyw0OjVdLCAyLCBtZWRpYW4pCgpteXN1bW1hcnkyLmRmWywgIklRUiJdIDwtIGFwcGx5KHN0b3JlLmRmWywgNDo1XSwgMiwgSVFSKQoKbXlzdW1tYXJ5Mi5kZlssICJNZWFuIFNhbGVzIl0gPC0gYXBwbHkoc3RvcmUuZGZbLCA0OjVdLCAyLCBtZWFuKQoKCm15c3VtbWFyeTIuZGZbLCAia3VydG9zaSJdIDwtIGFwcGx5KHN0b3JlLmRmWywgNDo1XSwgMiwgcHN5Y2g6Omt1cnRvc2kpCgoKbXlzdW1tYXJ5Mi5kZgoKYGBgCgojIyAzLjQgU2luZ2xlIFZhcmlhYmxlIFZpc3VhbGlzYXRpb24KCmBgYHtyfQoKaGlzdChzdG9yZS5kZiRwMXNhbGVzKQojYWRkIHRpdGxlIGFuZCBheGlzIGxhYmVscwoKIGhpc3Qoc3RvcmUuZGYkcDFzYWxlcywKICBtYWluPSJQcm9kdWN0IDEgV2Vla2x5IFNhbGVzIEZyZXF1ZW5jaWVzLCBBbGwgU3RvcmVzIiwKIHhsYWI9IlByb2R1Y3QgMSBTYWxlcyAoVW5pdHMpIiwKIHlsYWI9IkNvdW50IiApCiAKIAogIyBpbmNyZWFzZSBncmFudWxhcml0eSBieSBhZGRpbmcgYnJlYWtzICAgICAgICAgCmhpc3Qoc3RvcmUuZGYkcDFzYWxlcywKICAgICBtYWluPSJQcm9kdWN0IDEgV2Vla2x5IFNhbGVzIEZyZXF1ZW5jaWVzLCBBbGwgU3RvcmVzIiwKICAgICAgeGxhYj0iUHJvZHVjdCAxIFNhbGVzIChVbml0cykiLAogICAgICB5bGFiPSJDb3VudCIsCiAgICAgIGJyZWFrcz0zMCwgICAgICAgICAgICAgIyBtb3JlIGNvbHVtbnMKICAgICAgY29sPSJsaWdodGJsdWUiKSAgICAgICAjIGNvbG9yIHRoZSBiYXJzIAogCgpgYGAKCiMjIyMgVGhlIHktYXhpcyB2YWx1ZSBmb3IgdGhlIGhlaWdodCBvZiB0aGUgYmFycyBjaGFuZ2VzIGFjY29yZGluZyB0byBjb3VudC4gVGhlIGNvdW50IGRlcGVuZHMgb24gdGhlIG51bWJlciBvZiBiaW5zIGFuZCBvbiB0aGUgc2FtcGxlIHNpemUuIFdlIGNhbiBtYWtlIGl0IGFic29sdXRlIGJ5IHVzaW5nIHJlbGF0aXZlIGZyZXF1ZW5jaWVzICh0ZWNobmljYWxseSwgdGhlIGRlbnNpdHkgZXN0aW1hdGUpIGluc3RlYWQgb2YgY291bnRzIGZvciBlYWNoIHBvaW50LiBUaGlzIG1ha2VzIHRoZSBZIGF4aXMgY29tcGFyYWJsZSBhY3Jvc3MgZGlmZmVyZW50IHNpemVkIHNhbXBsZQoKYGBge3J9CgpoaXN0KHN0b3JlLmRmJHAxc2FsZXMsCiAgICAgIG1haW49IlByb2R1Y3QgMSBXZWVrbHkgU2FsZXMgRnJlcXVlbmNpZXMsIEFsbCBTdG9yZXMiLAogICAgICB4bGFiPSJQcm9kdWN0IDEgU2FsZXMgKFVuaXRzKSIsCiAgICB5bGFiPSJSZWxhdGl2ZSBmcmVxdWVuY3kiLAogYnJlYWtzPTMwLAogICAgICBjb2w9ImxpZ2h0Ymx1ZSIsCiAgICAgIGZyZXE9RkFMU0UsICAgICAgICAgICAgICAgICAgIyBmcmVxPUZBTFNFIG1lYW5zIHBsb3QgZGVuc2l0eSwgbm90IGNvdW50cwogICAgICB4YXh0PSJuIikgICAgICAgICAgICAgICAgICAgICMgeGF4dD0ibiIgbWVhbnMgInggYXhpcyB0aWNrIG1hcmtzID09IG5vIgoKCmxpbmVzKGRlbnNpdHkoc3RvcmUuZGYkcDFzYWxlcywgYnc9MTApLCAgICAjICJidz0gLi4uIiBhZGp1c3RzIHRoZSBzbW9vdGhpbmcKICAgICAgIHR5cGU9ImwiLCBjb2w9ImRhcmtyZWQiLCBsd2Q9MikgICAgICAjIGx3ZCA9IGxpbmUgd2lkdGgKCiNGaW5hbGx5LCB3ZSBhZGQgYSBzbW9vdGhlZCBlc3RpbWF0aW9uIGxpbmUuIFRvIGRvIHRoaXMsIHdlIHVzZSB0aGUgZGVuc2l0eSgpIGZ1bmN0aW9uIHRvIGVzdGltYXRlIGRlbnNpdHkgdmFsdWVzIGZvciB0aGUgcDFzYWxlcyB2ZWN0b3IsIGFuZCBhZGQgdGhvc2UgdG8gdGhlIGNoYXJ0IHdpdGggdGhlIGxpbmVzKCkgY29tbWFuZC4gVGhlIGxpbmVzKCkgY29tbWFuZCBhZGRzIGVsZW1lbnRzIHRvIHRoZSBjdXJyZW50IHBsb3QgaW4gdGhlIHNhbWUgd2F5IHdlIHNhdyBhYm92ZSBmb3IgdGhlIGF4aXMgY29tbWFuZC4KCmBgYAojIyMjIEZpbmFsbHksIHdlIGFkZCBhIHNtb290aGVkIGVzdGltYXRpb24gbGluZS4gVG8gZG8gdGhpcywgd2UgdXNlIHRoZSBkZW5zaXR5KCkgZnVuY3Rpb24gdG8gZXN0aW1hdGUgZGVuc2l0eSB2YWx1ZXMgZm9yIHRoZSBwMXNhbGVzIHZlY3RvciwgYW5kIGFkZCB0aG9zZSB0byB0aGUgY2hhcnQgd2l0aCB0aGUgbGluZXMoKSBjb21tYW5kLiBUaGUgbGluZXMoKSBjb21tYW5kIGFkZHMgZWxlbWVudHMgdG8gdGhlIGN1cnJlbnQgcGxvdCBpbiB0aGUgc2FtZSB3YXkgd2Ugc2F3IGFib3ZlIGZvciB0aGUgYXhpcyBjb21tYW5kLgoKCgojIyMgMy40LjIgQm94cGxvdHMgCgpgYGB7cn0KCmJveHBsb3Qoc3RvcmUuZGYkcDJzYWxlcywgeGxhYj0iV2Vla2x5IHNhbGVzIiwgeWxhYj0iUDIiLAogICAgICAgICAgbWFpbj0iV2Vla2x5IHNhbGVzIG9mIFAyLCBBbGwgc3RvcmVzIiwgaG9yaXpvbnRhbD1UUlVFKQoKYGBgCiMjIyMgQm94cGxvdHMgYXJlIGV2ZW4gbW9yZSB1c2VmdWwgd2hlbiB5b3UgY29tcGFyZSBkaXN0cmlidXRpb25zIGJ5IHNvbWUgb3RoZXIgZmFjdG9yLiBIb3cgZG8gZGlmZmVyZW50IHN0b3JlcyBjb21wYXJlIG9uIHNhbGVzIG9mIHByb2R1Y3QgMj8gVGhlIGJveHBsb3QoKSBjb21tYW5kIG1ha2VzIGl0IGVhc3kgdG8gY29tcGFyZSB0aGVzZSBieSBzcGVjaWZ5aW5nIGEgcmVzcG9uc2UgZm9ybXVsYSB1c2luZyB0aWxkZSBub3RhdGlvbiwgd2hlcmUgdGhlIHRpbGRlICjigJziiLzigJ0pIHNlcGFyYXRlcyB0aGUgcmVzcG9uc2UgdmFyaWFibGUgKHNvbWV0aW1lcyBjYWxsZWQgYSBkZXBlbmRlbnQgdmFyaWFibGUpIGZyb20gdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlIChzb21ldGltZXMgcmF0aGVyIG1pc2xlYWRpbmdseSBjYWxsZWQgYW4gaW5kZXBlbmRlbnQgdmFyaWFibGUpLiBJbiB0aGlzIGNhc2UsIG91ciByZXNwb25zZSB2YXJpYWJsZSBpcyBwMnNhbGVzIGFuZCB3ZSB3YW50IHRvIHBsb3QgaXQgd2l0aCByZWdhcmQgdG8gdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlIHN0b3JlTnVtLiBUaGlzIG1heSBiZSBlYXNpZXN0IHRvIHVuZGVyc3RhbmQgd2l0aCB0aGUgUiBjb2RlOgoKYGBge3IgZm9ybXVsYX0KIyBib3hwbG90IHAyc2FsZXMgYnkgU3RvcmUuCmJveHBsb3Qoc3RvcmUuZGYkcDJzYWxlcyB+IHN0b3JlLmRmJHN0b3JlTnVtLCBob3Jpem9udGFsPVRSVUUsCiAgeWxhYj0iU3RvcmUiLCB4bGFiPSJXZWVrbHkgdW5pdCBzYWxlcyIsIGxhcz0xLAogIG1haW49IldlZWtseSBTYWxlcyBvZiBQMiBieSBTdG9yZSIpCgpgYGAKCgojIyMjIEFib3ZlOiBUaGUgZmlyc3QgcG9ydGlvbiBvZiB0aGUgY29tbWFuZCBtYXkgYmUgcmVhZCBhcyDigJxib3hwbG90IHAyc2FsZXMgYnkgU3RvcmUu4oCdIEZvci0gbXVsYXMgbGlrZSB0aGlzIGFyZSBwZXJ2YXNpdmUgaW4gUiBhbmQgYXJlIHVzZWQgYm90aCBmb3IgcGxvdHRpbmcgYW5kIGZvciBlc3RpbWF0aW5nIG1vZGVscy4gV2UgZGlzY3VzcyBmb3JtdWxhcyBpbiBkZXRhaWwgaW4gU2VjdC4gNS4yLjEgYW5kIENoYXAuIDcuCgojIyMjIFdlIGFkZGVkIG9uZSBvdGhlciBhcmd1bWVudCB0byB0aGUgcGxvdDogbGFzPTEuIFRoYXQgZm9yY2VzIHRoZSBheGVzIHRvIGhhdmUgdGV4dCBpbiB0aGUgaG9yaXpvbnRhbCBkaXJlY3Rpb24sIG1ha2luZyB0aGUgc3RvcmUgbnVtYmVycyBtb3JlIHJlYWRhYmxlLiBUaGUgcmVzdWx0IGlzIEZpZy4gMy44LCB3aGVyZSBzdG9yZXMgYXJlIHJvdWdobHkgc2ltaWxhciBpbiBzYWxlcyBvZiBwcm9kdWN0IDIgKHRoaXMgaXMgbm90IGEgc3RhdGlzdGljYWwgdGVzdCBvZiBkaWZmZXJlbmNlLCBqdXN0IGEgdmlzdWFsaXphdGlvbikuCgpgYGB7cn0KCgojIGJ1dCBkbyBwMiBzYWxlcyBkaWZmZXIgaW4gcmVsYXRpb24gdG8gaW4tc3RvcmUgcHJvbW90aW9uCmJveHBsb3QocDJzYWxlcyB+IHAycHJvbSwgZGF0YT1zdG9yZS5kZiwgaG9yaXpvbnRhbD1UUlVFLCB5YXh0PSJuIiwKICB5bGFiPSJQMiBwcm9tb3RlZCBpbiBzdG9yZT8iLCB4bGFiPSJXZWVrbHkgc2FsZXMiLAogIG1haW49IldlZWtseSBzYWxlcyBvZiBQMiB3aXRoIGFuZCB3aXRob3V0IHByb21vdGlvbiIpCiAgYXhpcyhzaWRlPTQsIGF0PWMoMSwyKSwgbGFiZWxzPWMoIk5vIiwgIlllcyIpKQoKYGBgCgojIyMgMy40LjMgUVEgUGxvdCB0byBDaGVjayBOb3JtYWxpdHkKCmV2YWx1YXRlIGEgZGlzdHJpYnV0aW9uIG1vcmUgZm9ybWFsbHkKClF1YW50aWxl4oCTcXVhbnRpbGUgKFFRKSBwbG90cyBhcmUgYSBnb29kIHdheSB0byBjaGVjayBvbmXigJlzIGRhdGEgYWdhaW5zdCBhIGRpc3RyaWJ1dGlvbiB0aGF0IHlvdSB0aGluayBpdCBzaG91bGQgY29tZSBmcm9tLiBTb21lIGNvbW1vbiBzdGF0aXN0aWNzIHN1Y2ggYXMgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IHIgKHRvIGJlIHByZWNpc2UsIHRoZSBQZWFyc29uIHByb2R1Y3QtbW9tZW50IGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50KSBhcmUgaW50ZXJwcmV0ZWQgdW5kZXIgYW4gYXNzdW1wdGlvbiB0aGF0IGRhdGEgYXJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiBBIFFRIHBsb3QgY2FuIGNvbmZpcm0gdGhhdCB0aGUgZGlzdHJpYnV0aW9uIGlzLCBpbiBmYWN0LCBub3JtYWwgYnkgcGxvdHRpbmcgdGhlIG9ic2VydmVkIHF1YW50aWxlcyBvZiB5b3VyIGRhdGEgYWdhaW5zdCB0aGUgcXVhbnRpbGVzIHRoYXQgd291bGQgYmUgZXhwZWN0ZWQgZm9yIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4KCgpUbyBkbyB0aGlzLCB0aGUgcXFub3JtKCkgY29tbWFuZCBjb21wYXJlcyBkYXRhIHZzLiBhIG5vcm1hbCBkaXN0cmlidXRpb247IHlvdSBjYW4gdXNlIHFxbGluZSgpIHRvIGFkZCBhIGRpYWdvbmFsIGxpbmUgZm9yIGVhc2llciByZWFkaW5nLiBXZSBjaGVjayBwMXNhbGVzIHRvIHNlZSB3aGV0aGVyIGl0IGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkOgoKYGBge3J9CnFxbm9ybShzdG9yZS5kZiRwMXNhbGVzKQojbWFrZSBzdXJlIHlvdSBjYWxsIGEgcGxvdCBiZWZvcmUgY2FsbGluZyBxcWxpbmUKcXFsaW5lKHN0b3JlLmRmJHAxc2FsZXMpCgojZmFyIGZyb20gdGhlIGxpbmVzIGF0IHRoZSBlbmQgd2hpY2ggaW1wbGllcyB0aGUgZGF0YSBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQKYGBgCgojIyMjIGZhciBmcm9tIHRoZSBsaW5lcyBhdCB0aGUgZW5kIHdoaWNoIGltcGxpZXMgdGhlIGRhdGEgaXMgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkClRoZSB1cHdhcmQgY3VydmluZyBzaGFwZSBpcyB0eXBpY2FsIG9mIGRhdGEgd2l0aCBoaWdoIHBvc2l0aXZlIHNrZXcKCldoYXQgZG8geW91IGRvPz8KWW91IG1pZ2h0IHRyYW5zZm9ybSB5b3VyIGRheWEuIChsb2dhcml0aG1pYyBkaXN0Li4uKQoKYGBge3J9CnFxbm9ybShsb2coc3RvcmUuZGYkcDFzYWxlcykpCj9xcW5vcm0KI21ha2Ugc3VyZSB5b3UgY2FsbCBhIHBsb3QgYmVmb3JlIGNhbGxpbmcgcXFsaW5lCnFxbGluZShsb2coc3RvcmUuZGYkcDFzYWxlcykpCgojdGhlIHBvaW50cyBhcmUgbXVjaCBjbG9zZXIgdG8gdGhlIGxpbmUgZXNwZWNpYWxseSBvbiB0aGUgaGlnaGVyIGVuZAojIGltcGx5aW5nIHRoYXQgdGhlIGxvZyB0cmFuc2Zvcm0gaXMgbW9yZSBjb25zaXN0ZW50IHdpdGgKIyB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbgpgYGAKCgojIyMjIHRoZSBwb2ludHMgYXJlIG11Y2ggY2xvc2VyIHRvIHRoZSBsaW5lIGVzcGVjaWFsbHkgb24gdGhlIGhpZ2hlciBlbmQgaW1wbHlpbmcgdGhhdCB0aGUgbG9nIHRyYW5zZm9ybSBpcyBtb3JlIGNvbnNpc3RlbnQgd2l0aCB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbgoKIyMjIyBXaGVuIHRlc3RpbmcgYXNzdW1wdGlvbnMgYWJvdXQgeW91ciBkYXRhLCBxcW5vcm0gY2FuIGJlIHZlcnkgdXNlZnVsLiBSZXNlYXJjaCBtb3JlIG9uIGhvdyB0byBpbnRlcnByZXQgdGhlbS4KCiMjIyBDdW11bGF0aXZlIERpc3RyaWJ1dGlvbgpmb3JtYWxseSBjYWxsZWQgLSBlbXBpcmljYWwgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gKEVDREYpCgpOZXh0OgoKV2UgcGxvdCB0aGUgRUNERiBvZiBwMXNhbGVzIGJ5IGNvbWJpbmluZyBhIGZldyBzdGVwcy4gRmlyc3QsIHdlIHVzZSB0aGUgZWNkZigpIGZ1bmN0aW9uIHRvIGZpbmQgdGhlIEVDREYgb2YgdGhlIGRhdGEuIFRoZW4gd2Ugd3JhcCBwbG90KCkgYXJvdW5kIHRoYXQsIGFkZGluZyBvcHRpb25zIHN1Y2ggYXMgdGl0bGVzLiBOZXh0IHdlIHB1dCBzb21lIG5pY2VyLWxvb2tpbmcgbGFiZWxzIG9uIHRoZSBZIGF4aXMgdGhhdCByZWxhYmVsIHRoZSBwcm9wb3J0aW9ucyBhcyBwZXJjZW50aWxlcy4gVGhlIHBhc3RlKCkgZnVuY3Rpb24gY29tYmluZXMgYSBudW1iZXIgdmVjdG9yICgwLCAxMCwgMjAsIC4uLikgd2l0aCB0aGUg4oCcJeKAnSBzeW1ib2wgdG8gbWFrZSBlYWNoIGxhYmVsLgoKYGBge3J9CgoKcGxvdChlY2RmKHN0b3JlLmRmJHAxc2FsZXMpKQpwbG90KGVjZGYoc3RvcmUuZGYkcDFzYWxlcyksCiAgICAgIG1haW49IkN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIG9mIFAxIFdlZWtseSBTYWxlcyIsCiAgICAgIHlsYWI9IkN1bXVsYXRpdmUgUHJvcG9ydGlvbiIsCiAgICAgIHhsYWI9YygiUDEgd2Vla2x5IHNhbGVzLCBhbGwgc3RvcmVzIiwgIjkwJSBvZiB3ZWVrcyBzb2xkIDw9IDE3MSB1bml0cyIpLAogICAgICB5YXh0PSJuIikKIGF4aXMoc2lkZT0yLCBhdD1zZXEoMCwgMSwgYnk9MC4xKSwgbGFzPTEsCiAgICAgIGxhYmVscz1wYXN0ZShzZXEoMCwxMDAsYnk9MTApLCAiJSIsIHNlcD0iIikpCiBhYmxpbmUoaD0wLjksIGx0eT0zKSAgICAgICAgICAgIyAiaD0iIGZvciBob3Jpem9udGFsIGxpbmU7ICJsdHk9MyIgZm9yIGRvdHRlZAogYWJsaW5lKHY9cXVhbnRpbGUoc3RvcmUuZGYkcDFzYWxlcywgcHI9MC45KSwgbHR5PTMpICAjICJ2PSIgZm9yIHZlcnRpY2FsIGxpbmUKCmBgYAoKIyMgMy40LjUgTGFuZ3VhZ2UgQnJpZWY6IGJ5KCkgYW5kIGFnZ3JlZ2F0ZSgpCgpXaGF0IHNob3VsZCB3ZSBkbyBpZiB3ZSB3YW50IHRvIGJyZWFrIG91dCBkYXRhIGJ5IGZhY3RvcnMgYW5kIHN1bW1hcml6ZSBpdCwgYSBwcm9jZXNzIHlvdSBtaWdodCBrbm93IGFzIOKAnGNyb3NzLXRhYnPigJ0gb3Ig4oCccGl2b3QgdGFibGVz4oCdPwoKRm9yIGV4YW1wbGUsIGhvdyBjYW4gd2UgY29tcHV0ZSB0aGUgbWVhbiBzYWxlcyBieSBzdG9yZT8KCmJ5IGFuZCBhZ2dyZWdhdGUgY2FuIGhlbHAhCgpTdXBwb3NlIHdlIHdpc2ggdG8gZmluZCB0aGUgYXZlcmFnZSBzYWxlcyBvZiBQMSBieSBzdG9yZQoKVGhlIERBVEEgd291bGQgYmUgdGhlIHdlZWtseSBzYWxlcyBmb3IgZWFjaCBzdG9yZSwgc3RvcmUuZGYkcDFzYWxlcy4gV2Ugd2lzaCB0byBzcGxpdCB0aGlzIGJ5IHN0b3JlLCBzbyB0aGUgSU5ESUNFUyAoYWN0dWFsbHksIOKAnGluZGV44oCdIGluIHRoaXMgY2FzZSkgd291bGQgYmUgc3RvcmUuZGYkc3RvcmVOdW0uIEZpbmFsbHksIHdlIGdldCB0aGUgYXZlcmFnZSBvZiBlYWNoIG9mIHRob3NlIGdyb3VwcyBieSB1c2luZyB0aGUgbWVhbiBmdW5jdGlvbi4KYGBge3J9CiMgU3VwcG9zZSB3ZSB3aXNoIHRvIGZpbmQgdGhlIGF2ZXJhZ2Ugc2FsZXMgb2YgUDEgYnkgc3RvcmUKYnkoc3RvcmUuZGYkcDFzYWxlcywgc3RvcmUuZGYkc3RvcmVOdW0sIG1lYW4pCmJ5KHN0b3JlLmRmJHAxc2FsZXMsIGxpc3Qoc3RvcmUuZGYkc3RvcmVOdW0sIHN0b3JlLmRmJFllYXIpLCBtZWFuKQpgYGAKCkhvdyBkb2VzIHRoaXMgd29yaz8oQkVMT1cpIEp1c3QgYXMgd2l0aCBieSgpLCBhZ2dyZWdhdGUoeD1EQVRBLCBieT1CWSwgRlVOPUZVTkNUSU9OKSBhcHBsaWVzIGEgcGFydGljdWxhciBmdW5jdGlvbiAoRlVOKSBhY2NvcmRpbmcgdG8gZGl2aXNpb25zIG9mIHRoZSBkYXRhIHNwZWNpZmllZCBieSBhIGZhY3RvciAoYnkpLiBXZSB3YW50IHRvIGZpbmQgdGhlIHRvdGFsIHNhbGVzIGJ5IGNvdW50cnksIHNvIHdlIGFwcGx5IHRoZSBtZWFuIGZ1bmN0aW9uIGJ5IHN0b3JlLmRmJGNvdW50cnkuCgpgYGB7cn0KYWdncmVnYXRlKHN0b3JlLmRmJHAxc2FsZXMsIGJ5PWxpc3QoY291bnRyeT1zdG9yZS5kZiRjb3VudHJ5KSwgc3VtKQoKIHAxc2FsZXMuc3VtIDwtIGFnZ3JlZ2F0ZShzdG9yZS5kZiRwMXNhbGVzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJ5PWxpc3QoY291bnRyeT1zdG9yZS5kZiRjb3VudHJ5KSwgc3VtKQogCiBwMXNhbGVzLnN1bQoKYGBgCgoKIyMjIDMuNC42IE1hcHMgCm1hcmtldGluZyBkYXRhIG9uIGEgbWFwIC0gY2hvcm9wbGV0aCBtYXAKCkhlcmUgaXMgYSByb3V0aW5lIGV4YW1wbGUuIFN1cHBvc2UgdGhhdCB3ZSB3YW50IHRvIGNoYXJ0IHRoZSB0b3RhbCBzYWxlcyBieSBjb3VuLSB0cnkuIFdlIHVzZSBhZ2dyZWdhdGUoKSBhcyBpbiBTZWN0LiAzLjQuNSB0byBmaW5kIHRoZSB0b3RhbCBzYWxlcyBvZiBQMSBieSBjb3VudHJ5OgoKYGBge3J9CiBwMXNhbGVzLnN1bSA8LSBhZ2dyZWdhdGUoc3RvcmUuZGYkcDFzYWxlcywKICAgICAgICAgICAgICAgICAgICAgICAgIGJ5PWxpc3QoY291bnRyeT1zdG9yZS5kZiRjb3VudHJ5KSwgc3VtKQoKaW5zdGFsbC5wYWNrYWdlcyhjKCJyd29ybGRtYXAiLCAiUkNvbG9yQnJld2VyIikpICAjIGlmIG5lZWRlZApsaWJyYXJ5KHJ3b3JsZG1hcCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCgpgYGAKCgpGaXJzdCwgd2UgaGF2ZSB0byBhc3NvY2lhdGUgdGhlIGFnZ3JlZ2F0ZWQgZGF0YSB3aXRoIHNwZWNpZmljIG1hcCByZWdpb25zIHVzaW5nIHRoZSBjb3VudHJ5IGNvZGVzLiBUaGlzIGNhbiBiZSBkb25lIHdpdGggdGhlIGpvaW5Db3VudHJ5RGF0YTJNYXAoKSBmdW5jLSB0aW9uLCB3aGljaCBtYXRjaGVzIGNvdW50cnkgbG9jYXRpb25zIChzdG9yZS5kZiRjb3VudHJ5KSBmb3IgZGF0YSBwb2ludHMgd2l0aCB0aGUgY29ycmVzcG9uZGluZyBpbnRlcm5hdGlvbmFsIHN0YW5kYXJkIG5hbWVzIChJU08gbmFtZXMpIGFuZCByZXR1cm5zIGEgbWFwIG9iamVjdDoKCmBgYHtyfQoKcDFzYWxlcy5tYXAgPC0gam9pbkNvdW50cnlEYXRhMk1hcChwMXNhbGVzLnN1bSwgam9pbkNvZGUgPSAiSVNPMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lSm9pbkNvbHVtbiA9ICJjb3VudHJ5IikKbWFwQ291bnRyeURhdGEocDFzYWxlcy5tYXAsIG5hbWVDb2x1bW5Ub1Bsb3Q9IngiLAogbWFwVGl0bGU9IlRvdGFsIFAxIHNhbGVzIGJ5IENvdW50cnkiLAogY29sb3VyUGFsZXR0ZT1icmV3ZXIucGFsKDcsICJHcmVlbnMiKSwKIGNhdE1ldGhvZD0iZml4ZWRXaWR0aCIsIGFkZExlZ2VuZD1GQUxTRSkKCgoKYGBgCgojIFdlIGFja25vd2xlZGdlIHRoZSBuZWVkIGZvciBjYXV0aW9uIGRlc3BpdGUgdGhlIHBvcHVsYXJpdHkgb2Ygc3VjaCBtYXBzLgoKIyMgMy42IEtleSBQb2ludHMKVGhlIGZvbGxvd2luZyBndWlkZWxpbmVzIGFuZCBwb2ludGVycyB3aWxsIGhlbHAgeW91IHRvIGRlc2NyaWJlIGRhdGEgYWNjdXJhdGVseSBhbmQgcXVpY2tseToKCi0gQ29uc2lkZXIgc2ltdWxhdGluZyBkYXRhIGJlZm9yZSBjb2xsZWN0aW5nIGl0LCBpbiBvcmRlciB0byB0ZXN0IHlvdXIgYXNzdW1wdGlvbnMgYW5kIGRldmVsb3AgaW5pdGlhbCBhbmFseXNpcyBjb2RlIChTZWN0LiAzLjEpLgotIEFsd2F5cyBjaGVjayB5b3VyIGRhdGEgZm9yIHByb3BlciBzdHJ1Y3R1cmUgYW5kIGRhdGEgcXVhbGl0eSB1c2luZyBzdHIoKSwgaGVhZCgpLCBzdW1tYXJ5KCksIGFuZCBvdGhlciBiYXNpYyBpbnNwZWN0aW9uIGNvbW1hbmRzIChTZWN0LiAzLjMuMykuCi0gRGVzY3JpYmUgZGlzY3JldGUgKGNhdGVnb3JpY2FsKSBkYXRhIHdpdGggdGFibGUoKSAoU2VjdC4gMy4yLjEpIGFuZCBpbnNwZWN0IGNvbnRpbnVvdXMgZGF0YSB3aXRoIGRlc2NyaWJlKCkgZnJvbSB0aGUgcHN5Y2ggcGFja2FnZSAoU2VjdC4gMy4zLjIpLgotIEhpc3RvZ3JhbXMgKFNlY3QuIDMuNC4xKSBhbmQgYm94cGxvdHMgKFNlY3QuIDMuNC4yKSBhcmUgZ29vZCBmb3IgaW5pdGlhbCBkYXRhIHZpc3VhbGl6YXRpb24uCi0gVXNlIGJ5KCkgYW5kIGFnZ3JlZ2F0ZSgpIHRvIGJyZWFrIG91dCB5b3VyIGRhdGEgYnkgZ3JvdXBpbmcgdmFyaWFibGVzIChTZWN0LiAzLjQuNSkuCi0gQWR2YW5jZWQgdmlzdWFsaXphdGlvbiBtZXRob2RzIGluY2x1ZGUgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gKFNlY3QuIDMuNC40KSwgbm9ybWFsaXR5IGNoZWNrcyAoU2VjdC4gMy40LjMpLCBhbmQgbWFwcGluZyAoU2VjdC4gMy40LjYpLgoKIyA0IFJlbGF0aW9uc2hpcHMgQmV0d2VlbiBDb250aW5vdXMgVmFyaWFibGVzCgojIyMgNC4xLjEKCndlIGNyZWF0ZSBhIGRhdGEgc2V0IGZvciAqMSwwMDAgY3VzdG9tZXJzKiBvZiBhIHJldGFpbGVyIHdobyBzZWxscyBwcm9kdWN0cyBpbiBzdG9yZXMgYW5kIG9ubGluZS4gVGhpcyBkYXRhIGlzIHR5cGljYWwgb2Ygd2hhdCBvbmUgbWlnaHQgc2FtcGxlIGZyb20gYSBjb21wYW554oCZcyBjdXN0b21lciByZWxhdGlvbnNoaXAgbWFuYWdlbWVudCAoQ1JNKSBzeXN0ZW0uIFdlIGJlZ2luIGJ5IHNldHRpbmcgYSByYW5kb20gbnVtYmVyIHNlZWQgdG8gbWFrZSB0aGUgcHJvY2VzcyByZXBlYXRhYmxlIChhcyBkZXNjcmliZWQgaW4gU2VjdC4gMy4xLjIpIGFuZCBjcmVhdGluZyBhIGRhdGEgZnJhbWUgdG8gc3RvcmUgdGhlIGRhdGE6CgpgYGB7cn0KCnNldC5zZWVkKDIxODIxKQpuY3VzdCA8LSAxMDAwCmN1c3QuZGYgPC0gZGF0YS5mcmFtZShjdXN0LmlkPWFzLmZhY3RvcihjKDE6bmN1c3QpKSkKYGBgCgpOZXh0IHdlIGNyZWF0ZSBhIG51bWJlciBvZiB2YXJpYWJsZXMgZGVzY3JpYmluZyB0aGUgY3VzdG9tZXJzLCBhZGQgdGhvc2UgdmFyaWFibGVzIHRvIHRoZSBjdXN0LmRmIGRhdGEgZnJhbWUsIGFuZCBpbnNwZWN0IHRoZW0gd2l0aCBzdW1tYXJ5KCk6CgpgYGB7ciBjdXN0IGRlc2NyaXB0aW9uc30KCmN1c3QuZGYkYWdlIDwtIHJub3JtKG49bmN1c3QsIG1lYW49MzUsIHNkPTUpCmN1c3QuZGYkY3JlZGl0LnNjb3JlIDwtIHJub3JtKG49bmN1c3QsIG1lYW49MypjdXN0LmRmJGFnZSs2MjAsIHNkPTUwKQpjdXN0LmRmJGVtYWlsIDwtIGZhY3RvcihzYW1wbGUoYygieWVzIiwgIm5vIiksIHNpemU9bmN1c3QsIHJlcGxhY2U9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9iPWMoMC44LCAwLjIpKSkKY3VzdC5kZiRkaXN0YW5jZS50by5zdG9yZSA8LSBleHAocm5vcm0obj1uY3VzdCwgbWVhbj0yLCBzZD0xLjIpKQpzdW1tYXJ5KGN1c3QuZGYpCgoKYGBgCk91ciBmaW5hbCB2YXJpYWJsZSBmb3IgdGhlIGJhc2ljIENSTSBkYXRhIGlzIGRpc3RhbmNlLnRvLnN0b3JlLCB3aGljaCB3ZSBhc3N1bWUgZm9sbG93cyB0aGUgZXhwb25lbnRpYWwgb2YgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24uIFRoYXQgZ2l2ZXMgZGlzLSB0YW5jZXMgdGhhdCBhcmUgYWxsIHBvc2l0aXZlLCB3aXRoIG1hbnkgZGlzdGFuY2VzIHRoYXQgYXJlIHJlbGF0aXZlbHkgY2xvc2UgdG8gdGhlIG5lYXJlc3Qgc3RvcmUgYW5kIGZld2VyIHRoYXQgYXJlIGZhciBmcm9tIGEgc3RvcmUuIFRvIHNlZSB0aGUgZGlzdHJpYnV0aW9uIGZvciB5b3Vyc2VsZiwgdHJ5IGhpc3QoY3VzdC5kZiRkaXN0YW5jZS50by5zdG9yZSkuIAoKRm9ybWFsbHksIHdlIHNheSB0aGF0IGRpc3RhbmNlLnRvLnN0b3JlIGZvbGxvd3MgYSBsb2dub3JtYWwgZGlzdHJpYnV0aW9uLiAoVGhpcyBpcyBzdWZmaWNpZW50bHkgY29tbW9uIHRoYXQgdGhlcmUgaXMgYSBidWlsdC1pbiBmdW5jdGlvbiBjYWxsZWQgcmxub3JtKG4sIG1lYW5sb2csIHNkbG9nKSB0aGF0IGRvZXMgdGhlIHNhbWUgdGhpbmcgYXMgdGFraW5nIHRoZSBleHBvbmVudGlhbCBvZiBybm9ybSgpLikKCiMjIyA0LjEuMiBTaW11bGF0aW5nIE9ubGluZSBhbmQgSW4tU3RvcmUgU2FsZXMgRGF0YQoKT3VyIG5leHQgc3RlcCBpcyB0byBjcmVhdGUgZGF0YSBmb3IgdGhlIG9ubGluZSBzdG9yZTogMSB5ZWFyIHRvdGFscyBmb3IgZWFjaCBjdXN0b21lciBmb3Igb25saW5lIHZpc2l0cyBhbmQgdHJhbnNhY3Rpb25zLCBwbHVzIHRvdGFsIHNwZW5kaW5nLiBXZSBzaW11bGF0ZSB0aGUgbnVtYmVyIG9mIHZpc2l0cyB3aXRoIGEgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uLCBhIGRpc2NyZXRlIGRpc3RyaWJ1dGlvbiBvZnRlbiB1c2VkIHRvIG1vZGVsIGNvdW50cyBvZiBldmVudHMgb3ZlciB0aW1lLiBMaWtlIHRoZSBsb2dub3JtYWwgZGlzdHJpYnV0aW9uLCB0aGUgKm5lZ2F0aXZlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiogZ2VuZXJhdGVzIHBvc2l0aXZlIHZhbHVlcyBhbmQgaGFzIGEgbG9uZyByaWdodC1oYW5kIHRhaWwsIG1lYW5pbmcgdGhhdCBpbiBvdXIgZGF0YSBtb3N0IGN1c3RvbWVycyBtYWtlIHJlbGF0aXZlbHkgZmV3IHZpc2l0cyBhbmQgYSBmZXcgY3VzdG9tZXJzIG1ha2UgbWFueSB2aXNpdHMuIERhdGEgZnJvbSB0aGUgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uIGNhbiBiZSBnZW5lcmF0ZWQgdXNpbmcgcm5iaW5vbSgpOgpgYGB7ciBuZWdhdGl2ZSBiaW5vbWlhbCBkaXN0fQpjdXN0LmRmJG9ubGluZS52aXNpdHMgPC0gcm5iaW5vbShuY3VzdCwgc2l6ZT0wLjMsCiAgbXUgPSAxNSArIGlmZWxzZShjdXN0LmRmJGVtYWlsPT0ieWVzIiwgMTUsIDApCiAgLSAwLjcgKiAoY3VzdC5kZiRhZ2UtbWVkaWFuKGN1c3QuZGYkYWdlKSkpCgpgYGAKCgoKCkFib3ZlOiBXZSBtb2RlbCB0aGUgbWVhbiAobXUpIG9mIHRoZSBuZWdhdGl2ZSBiaW5vbWlhbCB3aXRoIGEgYmFzZWxpbmUgdmFsdWUgb2YgMTUuIFRoZSBzaXplIGFyZ3VtZW50IHNldHMgdGhlIGRlZ3JlZSBvZiBkaXNwZXJzaW9uICh2YXJpYXRpb24pIGZvciB0aGUgc2FtcGxlcy4gV2UgYWRkIGFuIGF2ZXJhZ2UgMTUgb25saW5lIHZpc2l0cyBmb3IgY3VzdG9tZXJzIHdobyBoYXZlIGFuIGVtYWlsIG9uIGZpbGUsIHVzaW5nIGlmZWxzZSgpIHRvIGdlbmVyYXRlIGEgdmVjdG9yIG9mIDAgb3IgMTUgYXMgYXBwcm9wcmlhdGUuIEZpbmFsbHksIHdlIGFkZCBvciBzdWJ0cmFjdCB2aXNpdHMgZnJvbSB0aGUgdGFyZ2V0IG1lYW4gYmFzZWQgb24gdGhlIGN1c3RvbWVy4oCZcyBhZ2UgcmVsYXRpdmUgdG8gdGhlIHNhbXBsZSBtZWRpYW47IGN1c3RvbWVycyB3aG8gYXJlIHlvdW5nZXIgYXJlIHNpbXVsYXRlZCB0byBtYWtlIG1vcmUgb25saW5lIHZpc2l0cy4gVG8gc2VlIGV4YWN0bHkgaG93IHRoaXMgd29ya3MsIHRyeSBjdXR0aW5nIGFuZCBwYXN0aW5nIHBpZWNlcyBvZiB0aGUgY29kZSBhYm92ZSBpbnRvIHRoZSBSIGNvbnNvbGUuCgpGb3IgZWFjaCBvbmxpbmUgdmlzaXQgdGhhdCBhIGN1c3RvbWVyIG1ha2VzLCB3ZSBhc3N1bWUgdGhlcmUgaXMgYSAzMCAlIGNoYW5jZSBvZiBwbGFjaW5nIGFuIG9yZGVyIGFuZCB1c2UgcmJpbm9tKCkgdG8gY3JlYXRlIHRoZSB2YXJpYWJsZSBvbmxpbmUudHJhbnMuIFdlIGFzc3VtZSB0aGF0IGFtb3VudHMgc3BlbnQgaW4gdGhvc2Ugb3JkZXJzICh0aGUgdmFyaWFibGUgb25saW5lLnNwZW5kKSBhcmUgbG9nbm9ybWFsbHkgZGlzdHJpYnV0ZWQ6CgpgYGB7ciBjdXN0b21lciB0cmFucyBhbmQgc3BlbmR9CgogY3VzdC5kZiRvbmxpbmUudHJhbnMgPC0gcmJpbm9tKG5jdXN0LCBzaXplPWN1c3QuZGYkb25saW5lLnZpc2l0cywgcHJvYj0wLjMpCiBjdXN0LmRmJG9ubGluZS5zcGVuZCA8LSBleHAocm5vcm0obmN1c3QsIG1lYW49Mywgc2Q9MC4xKSkgKgogICAgICAgICAgICAgICAgICAgICAgICAgY3VzdC5kZiRvbmxpbmUudHJhbnMKIAogaGlzdChjdXN0LmRmJG9ubGluZS50cmFucykKIGhpc3QoY3VzdC5kZiRvbmxpbmUuc3BlbmQpCiAKICMgVGhlIHJhbmRvbSB2YWx1ZSBmb3IgYW1vdW50IHNwZW50IHBlciB0cmFuc2FjdGlvbuKAlHNhbXBsZWQgd2l0aCBleHAocm5vcm0oKSkgaXMgbXVsdGlwbGllZCBieSB0aGUgdmFyaWFibGUgZm9yIG51bWJlciBvZiB0cmFuc2FjdGlvbnMgdG8gZ2V0IHRoZSB0b3RhbCBhbW91bnQgc3BlbnQuCgpgYGAKCkFib3ZlOiBUaGUgcmFuZG9tIHZhbHVlIGZvciBhbW91bnQgc3BlbnQgcGVyIHRyYW5zYWN0aW9u4oCUc2FtcGxlZCB3aXRoIGV4cChybm9ybSgpKSBpcyBtdWx0aXBsaWVkIGJ5IHRoZSB2YXJpYWJsZSBmb3IgbnVtYmVyIG9mIHRyYW5zYWN0aW9ucyB0byBnZXQgdGhlIHRvdGFsIGFtb3VudCBzcGVudC4KCk5leHQgd2UgZ2VuZXJhdGUgaW4tc3RvcmUgc2FsZXMgZGF0YSBzaW1pbGFybHksIGV4Y2VwdCB0aGF0IHdlIGRvbuKAmXQgZ2VuZXJhdGUgYSBjb3VudCBvZiBzdG9yZSB2aXNpdHM7IGZldyBjdXN0b21lcnMgdmlzaXQgYSBwaHlzaWNhbCBzdG9yZSB3aXRob3V0IG1ha2luZyBhIHB1cmNoYXNlIGFuZCBldmVuIGlmIGN1c3RvbWVycyBkaWQgdmlzaXQgd2l0aG91dCBidXlpbmcsIHRoZSBjb21wYW55IHByb2JhYmx5IGNvdWxkbuKAmXQgdHJhY2sgdGhlIHZpc2l0LiBXZSBhc3N1bWUgdGhhdCB0cmFuc2FjdGlvbnMgZm9sbG93IGEgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uLCB3aXRoIGxvd2VyIGF2ZXJhZ2UgbnVtYmVycyBvZiB2aXNpdHMgZm9yIGN1c3RvbWVycyB3aG8gbGl2ZSBmYXJ0aGVyIGF3YXkuIFdlIG1vZGVsIGluLXN0b3JlIHNwZW5kaW5nIGFzIGEgbG9nbm9ybWFsbHkgZGlzdHJpYnV0ZWQgdmFyaWFibGUgc2ltcGx5IG11bHRpcGxpZWQgYnkgdGhlIG51bWJlciBvZiB0cmFuc2FjdGlvbnM6CgpgYGB7ciBpbi1zdG9yZSBzYWxlcyBkYXRhfQojIGxvd2VyIGF2ZXJhZ2UgbnVtYmVyIG9mIHZpc2l0cyBpZiB0aGV5IGxpdmUgZnVydGhlciBhd2F5CmN1c3QuZGYkc3RvcmUudHJhbnMgPC0gcm5iaW5vbShuY3VzdCwgc2l6ZT01LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11PTMgLyBzcXJ0KGN1c3QuZGYkZGlzdGFuY2UudG8uc3RvcmUpKQoKIyBsb2dub3JtYWxseSBkaXN0cmlidXRlZCB2YXIgKiBieSB0aGUgbnVtYmVyIG9mIHRyYW5zYWN0aW9ucwogY3VzdC5kZiRzdG9yZS5zcGVuZCA8LSBleHAocm5vcm0obmN1c3QsIG1lYW49My41LCBzZD0wLjQpKSAqCiAgICAgICAgICAgICAgICAgICAgICAgIGN1c3QuZGYkc3RvcmUudHJhbnMKIAogCiBoaXN0KGN1c3QuZGYkc3RvcmUudHJhbnMsIHBsb3QgPSBUUlVFKQogc3VtbWFyeShjdXN0LmRmKQogaGlzdChjdXN0LmRmJHN0b3JlLnNwZW5kLCBwbG90ID0gVFJVRSkKIAogc3VtbWFyeShjdXN0LmRmKQoKYGBgCgoKCiMjIDQuMS4zIFNpbXVsYXRpbmcgU2F0aXNmYWN0aW9uIFN1cnZleSBSZXNwb25zZXMKClRvIHNpbXVsYXRlIHN1cnZleSByZXNwb25zZXMsIHdlIGFzc3VtZSB0aGF0IGVhY2ggY3VzdG9tZXIgaGFzIGFuIHVub2JzZXJ2ZWQgb3ZlcmFsbCBzYXRpc2ZhY3Rpb24gd2l0aCB0aGUgYnJhbmQuIFdlIGdlbmVyYXRlIHRoaXMgb3ZlcmFsbCBzYXRpc2ZhY3Rpb24gZnJvbSBhIG5vci0gbWFsIGRpc3RyaWJ1dGlvbjoKCmBgYHtyIHNpbXVsYXRpbmcgc3VydmV5IHJlc3BvbnNlc30KCnNhdC5vdmVyYWxsIDwtIHJub3JtKG5jdXN0LCBtZWFuPTMuMSwgc2Q9MC43KQpzdW1tYXJ5KHNhdC5vdmVyYWxsKQpoaXN0KHNhdC5vdmVyYWxsKQoKYGBgCgpXZSBhc3N1bWUgdGhhdCBvdmVyYWxsIHNhdGlzZmFjdGlvbiBpcyBhIHBzeWNob2xvZ2ljYWwgY29uc3RydWN0IHRoYXQgaXMgbm90IGRpcmVjdGx5IG9ic2VydmFibGUuIEluc3RlYWQsIHRoZSBzdXJ2ZXkgY29sbGVjdHMgaW5mb3JtYXRpb24gb24gdHdvIGl0ZW1zOiBzYXRpc2ZhY3Rpb24gd2l0aCBzZXJ2aWNlLCBhbmQgc2F0aXNmYWN0aW9uIHdpdGggdGhlIHNlbGVjdGlvbiBvZiBwcm9kdWN0cy4gV2UgYXNzdW1lIHRoYXQgY3VzdG9tZXJz4oCZIHJlc3BvbnNlcyB0byB0aGUgc3VydmV5IGl0ZW1zIGFyZSBiYXNlZCBvbiB1bm9ic2VydmVkIGxldmVscyBvZiBzYXRpc2ZhY3Rpb24gb3ZlcmFsbCAoc29tZXRpbWVzIGNhbGxlZCB0aGUg4oCcaGFsb+KAnSBpbiBzdXJ2ZXkgcmVzcG9uc2UpIHBsdXMgdGhlIHNwZWNpZmljIGxldmVscyBvZiBzYXRpc2ZhYy0gdGlvbiB3aXRoIHRoZSBzZXJ2aWNlIGFuZCBwcm9kdWN0IHNlbGVjdGlvbi4KClRvIGNyZWF0ZSBzdWNoIGEgc2NvcmUgZnJvbSBhIGhhbG8gdmFyaWFibGUsIHdlIGFkZCBzYXQub3ZlcmFsbCAodGhlIGhhbG8pIHRvIGEgcmFuZG9tIHZhbHVlIHNwZWNpZmljIHRvIHRoZSBpdGVtLCBkcmF3biB1c2luZyBybm9ybSgpLiBCZWNhdXNlIHN1cnZleSByZXNwb25zZXMgYXJlIHR5cGljYWxseSBnaXZlbiBvbiBhIGRpc2NyZXRlLCBvcmRpbmFsIHNjYWxlIChpLmUuLCDigJx2ZXJ5IHVuc2F0aXNmaWVk4oCdLCDigJx1bnNhdGlzZmllZOKAnSwgZXRjLiksIHdlIGNvbnZlcnQgb3VyIGNvbnRpbnVvdXMgcmFuZG9tIHZhbHVlcyB0byBkaXNjcmV0ZSBpbnRlZ2VycyB1c2luZyB0aGUgZmxvb3IoKSBmdW5jdGlvbi4KCmBgYHtyIGhhbG8gdmFyaWFibGV9CgpzYXQuc2VydmljZSA8LSBmbG9vcihzYXQub3ZlcmFsbCArIHJub3JtKG5jdXN0LCBtZWFuPTAuNSwgc2Q9MC40KSkKc2F0LnNlbGVjdGlvbiA8LSBmbG9vcihzYXQub3ZlcmFsbCArIHJub3JtKG5jdXN0LCBtZWFuPS0wLjIsIHNkPTAuNikpCgoKc3VtbWFyeShjYmluZChzYXQuc2VydmljZSwgc2F0LnNlbGVjdGlvbikpCgpgYGAKYGBge3IgIHNlcnZpY2Ugc3VydmV5fQoKaGlzdChzYXQuc2VydmljZSkKYGBgCgoKYGBge3IgIHNlbGVjdGlvbiBzdXJ2ZXl9Cmhpc3Qoc2F0LnNlbGVjdGlvbikKYGBgCgpOb3RlIHRoYXQgd2UgdXNlIGNiaW5kKCkgdG8gdGVtcG9yYXJpbHkgY29tYmluZSBvdXIgdHdvIHZlY3RvcnMgb2YgZGF0YSBpbnRvIGEgbWF0cml4LCBzbyB0aGF0IHdlIGNhbiBnZXQgYSBjb21iaW5lZCBzdW1tYXJ5IHdpdGggYSBzaW5nbGUgbGluZSBvZiBjb2RlLiBUaGUgc3VtLSBtYXJ5IHNob3dzIHRoYXQgb3VyIGRhdGEgbm93IHJhbmdlcyBmcm9tIOKIkjEgdG8gNi4gSG93ZXZlciwgYSB0eXBpY2FsIHNhdGlzZmFjdGlvbiBpdGVtIG1pZ2h0IGJlIGdpdmVuIG9uIGEgNS1wb2ludCBzY2FsZS4gVG8gZml0IHRoYXQsIHdlIHJlcGxhY2UgdmFsdWVzIHRoYXQgYXJlIGdyZWF0ZXIgdGhhbiA1IHdpdGggNSwgYW5kIHZhbHVlcyB0aGF0IGFyZSBsZXNzIHRoYW4gMSB3aXRoIDEuIFRoaXMgZW5mb3JjZXMgdGhlIGZsb29yIGFuZCBjZWlsaW5nIGVmZmVjdHMgb2Z0ZW4gbm90ZWQgaW4gc3VydmV5IHJlc3BvbnNlIGxpdGVyYXR1cmUuCgoKV2Ugc2V0IHRoZSBjZWlsaW5nIGJ5IGluZGV4aW5nIHdpdGggYSB2ZWN0b3IgdGhhdCB0ZXN0cyB3aGV0aGVyIGVhY2ggZWxlbWVudCBvZiBzYXQuc2VydmljZWlzZ3JlYXRlcnRoYW41KTpzYXQuc2VydmljZVtzYXQuc2VydmljZSA+IDVdLlRoaXMgbWlnaHQgYmUgcmVhZCBhcyDigJxzYXQuc2VydmljZSwgd2hlcmUgc2F0LnNlcnZpY2UgaXMgZ3JlYXRlciB0aGFuIDUu4oCdIEZvciB0aGUgZWxlbWVudHMgdGhhdCBhcmUgc2VsZWN0ZWTigJR3aGljaCBtZWFucyB0aGF0IHRoZSBleHByZXNzaW9uIGV2YWx1YXRlcyBhcyBUUlVF4oCUd2UgcmVwbGFjZSB0aGUgY3VycmVudCB2YWx1ZXMgd2l0aCB0aGUgY2VpbGluZyB2YWx1ZSBvZiA1LiBXZSBkbyB0aGUgc2FtZSBmb3IgdGhlIGZsb29yIGVmZmVjdHMgKDwgMSxyZXBsYWNpbmd3aXRoMSlhbmRsaWtld2lzZWZvcnRoZWNlaWxpbmdhbmRmbG9vcm9mc2F0LnNlbGVjdGlvbi4gV2hpbGUgdGhpcyBzb3VuZHMgcXVpdGUgY29tcGxpY2F0ZWQsIHRoZSBjb2RlIGlzIHNpbXBsZToKCgpgYGB7ciBjZWlsaW5nIGZsb29yfQojIHNob3VsZCBiZSByZWFkIGFzIHNhdC5zZXJ2aWNlLCB3aGVyZSBzYXQuc2VydmljZSBpcyBncmVhdGVyIHRoYW4gNSwgc2V0IHRvIDUKc2F0LnNlcnZpY2Vbc2F0LnNlcnZpY2UgPiA1XSA8LSA1CiMgc2hvdWxkIGJlIHJlYWQgYXMgc2F0LnNlcnZpY2UsIHdoZXJlIHNhdC5zZXJ2aWNlIGlzIGxlc3MgdGhhbiAxLCBzZXQgdG8gMQpzYXQuc2VydmljZVtzYXQuc2VydmljZSA8IDFdIDwtIDEKc2F0LnNlbGVjdGlvbltzYXQuc2VsZWN0aW9uID4gNV0gPC0gNQpzYXQuc2VsZWN0aW9uW3NhdC5zZWxlY3Rpb24gPCAxXSA8LSAxCnN1bW1hcnkoY2JpbmQoc2F0LnNlcnZpY2UsIHNhdC5zZWxlY3Rpb24pKQoKYGBgCgoKCiMjIyA0LjEuNCBTaW11bGF0aW5nIE5vbi1SZXNwb25zZSBEYXRhCgpCZWNhdXNlIHNvbWUgY3VzdG9tZXJzIGRvIG5vdCByZXNwb25kIHRvIHN1cnZleXMsIHdlIGVsaW1pbmF0ZSB0aGUgc2ltdWxhdGVkIGFuLSBzd2VycyBmb3IgYSBzdWJzZXQgb2YgcmVzcG9uZGVudHMgd2hvIGFyZSBtb2RlbGVkIGFzIG5vdCBhbnN3ZXJpbmcuIFdlIGRvIHRoaXMgYnkgY3JlYXRpbmcgYSB2YXJpYWJsZSBvZiBUUlVFIGFuZCBGQUxTRSB2YWx1ZXMgY2FsbGVkIG5vLnJlc3BvbnNlIGFuZCB0aGVuIGFzLSBzaWduaW5nIGEgdmFsdWUgb2YgTkEgZm9yIHRoZSBzdXJ2ZXkgcmVzcG9uc2UgZm9yIGN1c3RvbWVycyB3aG9zZSBuby5yZXNwb25zZSBpcyBUUlVFLiBBcyB3ZSBoYXZlIGRpc2N1c3NlZCwgTkEgaXMgUuKAmXMgYnVpbHQtaW4gY29uc3RhbnQgZm9yIG1pc3NpbmcgZGF0YS4KCldlIG1vZGVsIG5vbi1yZXNwb25zZSBhcyBhIGZ1bmN0aW9uIG9mIGFnZSwgd2l0aCBoaWdoZXIgbGlrZWxpaG9vZCBvZiBub3QgcmVzcG9uZGluZyB0byB0aGUgc3VydmV5IGZvciBvbGRlciBjdXN0b21lcnM6CgpgYGB7ciBTaW11bGF0aW5nIE5vbi1SZXNwb25zZSBEYXRhfQoKbm8ucmVzcG9uc2UgPC0gYXMubG9naWNhbChyYmlub20obmN1c3QsIHNpemU9MSwgcHJvYj1jdXN0LmRmJGFnZS8xMDApKQojIHRoaW5raW5nIGZ1bmN0aW9uYWxseSBvbmNlIGFnYWluLCBnaXZlbiBuby5yZXNwb25zZSBpcyBUUlVFLCB3ZSBzZXQgc2VydmljZSBhbmQgc2VsZWN0aW9uIHRvIE5BIGFzIGEgbm9uIHJlc3BvbnNlCnNhdC5zZXJ2aWNlW25vLnJlc3BvbnNlXSA8LSBOQQpzYXQuc2VsZWN0aW9uW25vLnJlc3BvbnNlXSA8LSBOQQpzdW1tYXJ5KGNiaW5kKHNhdC5zZXJ2aWNlLCBzYXQuc2VsZWN0aW9uKSkKCmBgYAoKc3VtbWFyeSByZWNvZ25pc2VzIHRoYXQgMzQxIGN1c3RvbWVycyB3aXRoIE5BIHZhbHVlcyBhbmQgZXhjbHVkZXMgdGhlbSBmcm9tIHRoZSBzdGF0aXN0aWNzCgpUaGVuIHdlIGFkZCB0aGUgc3VydmV5IHJlc3BvbnNlcyB0byBjdXN0LmRmIGFuZCBjbGVhbiB1cCB0aGUgd29ya3NwYWNlLiAKYGBge3IgQWRkaW5nIHN1cnZleSByZXNwb25zZXMgfQpjdXN0LmRmJHNhdC5zZXJ2aWNlIDwtIHNhdC5zZXJ2aWNlCmN1c3QuZGYkc2F0LnNlbGVjdGlvbiA8LSBzYXQuc2VsZWN0aW9uCnN1bW1hcnkoY3VzdC5kZikKYGBgCgpXZSBhcmUgcmVhZHkgZm9yIGFuYWx5c2lzIQoKIyMgNC4yIEV4cGxvcmluZyBBc3NvY2lhdGlvbnMgQmV0d2VlbiBWYXJpYWJsZXMgd2l0aCBTY2F0dGVycGxvdHMKCmBgYHtyIHJldmlldyBzdHJ1Y3R1cmV9CgpzdHIoY3VzdC5kZikKCmBgYAoKQWRkaXRpb25hbCB2YXJpYWJsZXMgcmVwb3J0IDEteWVhciB0b3RhbCB2aXNpdHMgdG8gdGhlIG9ubGluZSBzaXRlCgojIyMgNC4yLjEgU2NhdHRlcnBsb3QKCmBgYHtyfQoKcGxvdCh4PWN1c3QuZGYkYWdlLCB5PWN1c3QuZGYkY3JlZGl0LnNjb3JlKQoKcGxvdChjdXN0LmRmJGFnZSwgY3VzdC5kZiRjcmVkaXQuc2NvcmUsCiAgICAgY29sPSJibHVlIiwKICAgICB4bGltPWMoMTUsIDU1KSwgeWxpbT1jKDUwMCwgOTAwKSwKICAgICBtYWluPSJBY3RpdmUgQ3VzdG9tZXJzIGFzIG9mIEp1bmUgMjAxNCIsCiAgICAgeGxhYj0iQ3VzdG9tZXIgQWdlICh5ZWFycykiLCB5bGFiPSJDdXN0b21lciBDcmVkaXQgU2NvcmUgIikKYWJsaW5lKGg9bWVhbihjdXN0LmRmJGNyZWRpdC5zY29yZSksIGNvbD0iZGFyayBibHVlIiwgbHR5PSJkb3R0ZWQiKQphYmxpbmUodj1tZWFuKGN1c3QuZGYkYWdlKSwgY29sPSJkYXJrIGJsdWUiLCBsdHk9ImRvdHRlZCIpCgpgYGAKCgpSIGxvb2tzIGF0IHdoYXQgdHlwZSBvZiBkYXRhIHlvdSBhcmUgdHJ5aW5nIHRvIHBsb3QgYW5kLCBiYXNlZCBvbiB0aGUgZGF0YSB0eXBlLCBSIHdpbGwgY2hvb3NlIGEgc3BlLSBjaWZpYyBsb3dlci1sZXZlbCBwbG90dGluZyBmdW5jdGlvbiwga25vd24gYXMgYSBtZXRob2QsIHRoYXQgaXMgYXBwcm9wcmlhdGUgdG8gdGhlIGRhdGEgeW91IGFyZSB0cnlpbmcgdG8gcGxvdC4gV2hlbiB3ZSBjYWxsIHBsb3QoKSB3aXRoIHZlY3RvcnMgb2YgeCBhbmQgeSBjb29yZGluYXRlcywgUiB1c2VzIHRoZSBwbG90LmRlZmF1bHQoKSBmdW5jdGlvbi4gSG93ZXZlciwgdGhlcmUgYXJlIG1hbnkgb3RoZXIgcGxvdHRpbmcgZnVuY3Rpb25zIGZvciBkaWZmZXJlbnQgZGF0YSB0eXBlcy4gRm9yIGV4YW1wbGUsIGlmIHlvdSBwbG90IHRoZSBjdXN0LmRmIGRhdGEgZnJhbWUgYnkgdHlwaW5nIHBsb3QoY3VzdC5kZikgaW50byB0aGUgY29uc29sZSwgUiB3aWxsIHVzZSBwbG90LmRhdGEuZnJhbWUoKSBpbnN0ZWFkIG9mIHBsb3QuZGVmYXVsdCgpLiBUaGlzIHByb2R1Y2VzIG9uZSBvZiBzZXZlcmFsIHBsb3QgdHlwZXMgZGVwZW5kaW5nIG9uIHRoZSBudW1iZXIgb2YgZGltZW5zaW9ucyBpbiB0aGUgZGF0YSBmcmFtZTsgaW4gdGhpcyBjYXNlLCBpdCBwcm9kdWNlcyBhIHNjYXR0ZXJwbG90IG1hdHJpeCwgd2hpY2ggd2UgcmV2aWV3IGluIFNlY3QuIDQuNC4yLgoKdCBtYXkgYmUgYmVjYXVzZSBSIGhhcyBzZWxlY3RlZCBhIGRpZmZlcmVudCBtZXRob2QgdGhhbiB5b3UgZXhwZWN0LiBJZiBzbywgY2hlY2sgdGhlIGRhdGEgdHlwZXMgb2YgdGhlIHZhcmktIGFibGVzIHlvdeKAmXJlIHNlbmRpbmcgdG8gcGxvdCgpIGJlY2F1c2UgUiB1c2VzIHRob3NlIHRvIHNlbGVjdCBhIHBsb3QgbWV0aG9kLgoKYGBge3IgcGxvdCBtZXRob2RzfQptZXRob2RzKHBsb3QpCgpgYGAKCgoKaW4gb3VyIGRhdGEsIGRvIGN1c3RvbWVycyB3aG8gYnV5IG1vcmUgb25saW5lIGJ1eSBsZXNzIGluIHN0b3Jlcz8gV2Ugc3RhcnQgYnkgcGxvdHRpbmcgb25saW5lIHNhbGVzIGFnYWluc3QgaW4tc3RvcmUgc2FsZXM6CgoKYGBge3J9CgpwbG90KGN1c3QuZGYkc3RvcmUuc3BlbmQsIGN1c3QuZGYkb25saW5lLnNwZW5kLAogICAgIG1haW49IkN1c3RvbWVycyBhcyBvZiBKdW5lIDIwMTQiLAogICAgIHhsYWI9IlByaW9yIDEyIG1vbnRocyBpbi1zdG9yZSBzYWxlcyAoJCkiLAogICAgIHlsYWI9IlByaW9yIDEyIG1vbnRocyBvbmxpbmUgc2FsZXMgKCQpIiwKICAgICBjZXg9MC4yKQoKYGBgCgpBYm92ZTogVGhlIHJlc3VsdGluZyBwbG90IGluIEZpZy4gNC4yIGlzIHR5cGljYWwgb2YgdGhlIHNrZXdlZCBkaXN0cmlidXRpb25zIHRoYXQgYXJlIGNvbW1vbiBpbiBiZWhhdmlvcmFsIGRhdGEgc3VjaCBhcyBzYWxlcyBvciB0cmFuc2FjdGlvbiBjb3VudHM7IG1vc3QgY3VzdG9tZXJzIHB1cmNoYXNlIHJhcmVseSBzbyB0aGUgZGF0YSBpcyBkZW5zZSBuZWFyIHplcm8uIFRoZSByZXN1bHRpbmcgcGxvdCBoYXMgYSBsb3Qgb2YgcG9pbnRzIGFsb25nIHRoZSBheGVzOyB3ZSB1c2UgdGhlIGNleCBvcHRpb24sIHdoaWNoIHNjYWxlcyBkb3duIHRoZSBwbG90dGVkIHBvaW50cyB0byAwLjcgb2YgdGhlaXIgZGVmYXVsdCBzaXplIHNvIHRoYXQgd2UgY2FuIHNlZSB0aGUgcG9pbnRzIGEgYml0IG1vcmUgY2xlYXJseS4gVGhlIHBsb3Qgc2hvd3MgdGhhdCB0aGVyZSBhcmUgYSBsYXJnZSBudW1iZXIgb2YgY3VzdG9tZXJzIHdobyBkaWRu4oCZdCBidXkgYW55dGhpbmcgb24gb25lIG9mIHRoZSB0d28gY2hhbm5lbHMgKHRoZSBwb2ludHMgYWxvbmcgdGhlIGF4ZXMpLCBhbG9uZyB3aXRoIGEgc21hbGxlciBudW1iZXIgb2YgY3VzdG9tZXJzIHdobyBwdXJjaGFzZSBmYWlybHkgbGFyZ2UgYW1vdW50cyBvbiBvbmUgb2YgdGhlIGNoYW5uZWxzLgoKQmVjYXVzZSBvZiB0aGUgc2tld2VkbmVzcyBvZiB0aGUgZGF0YSB3ZSB1c2UgYSBoaXN0b2dyYW0gaW4gaG9wZXMgb2YgYmV0dGVyIGNsYWlydHkuIAoKYGBge3IgaGlzdG9ncmFtIGluIHN0b3JlIHZzIG9ubGluZSBzYWxlcyB9CgpoaXN0KGN1c3QuZGYkc3RvcmUuc3BlbmQsCiAgYnJlYWtzPSgwOmNlaWxpbmcobWF4KGN1c3QuZGYkc3RvcmUuc3BlbmQpLzEwKSkqMTAsCiAgbWFpbj0iQ3VzdG9tZXJzIGFzIG9mIEp1bmUgMjAxNCIsCiAgeGxhYj0iUHJpb3IgMTIgbW9udGhzIG9ubGluZSBzYWxlcyAoJCkiLAogIHlsYWI9IkNvdW50IG9mIGN1c3RvbWVycyIpCgpgYGAKClRoZSBoaXN0b2dyYW0gaW4gRmlnLiA0LjMgc2hvd3MgY2xlYXJseSB0aGF0IGEgbGFyZ2UgbnVtYmVyIG9mIGN1c3RvbWVycyBib3VnaHQgbm90aGluZyBpbiB0aGUgb25saW5lIHN0b3JlIChhYm91dCA0MDAgb3V0IG9mIDEsMDAwKS4gVGhlIGRpc3RyaWJ1dGlvbiBvZiBzYWxlcyBhbW9uZyB0aG9zZSB3aG8gZG8gYnV5IGhhcyBhIG1vZGUgYXJvdW5kICQyMCBhbmQgYSBsb25nIHJpZ2h0LWhhbmQgdGFpbCB3aXRoIGEgZmV3IGN1c3RvbWVycyB3aG9zZSAxMi1tb250aCBzcGVuZGluZyB3YXMgaGlnaC4gU3VjaCBkaXN0cmlidXRpb25zIGFyZSB0eXBpY2FsIG9mIHNwZW5kaW5nIGFuZCB0cmFuc2FjdGlvbiBjb3VudHMgaW4gY3VzdG9tZXIgZGF0YS4KCgojIyMgNC4yLjIgQ29sb3ItQ29kaW5nIFBvaW50cyBvbiBhIFNjYXR0ZXJwbG90CkFub3RoZXIgcXVlc3Rpb24gaXMgd2hldGhlciB0aGUgcHJvcGVuc2l0eSB0byBidXkgb25saW5lIHZlcnN1cyBpbiBzdG9yZSBpcyByZWxhdGVkIHRvIG91ciBlbWFpbCBlZmZvcnRzICAoYXMgcmVmbGVjdGVkIGJ5IHdoZXRoZXIgb3Igbm90IGEgY3VzdG9tZXIgaGFzIGFuIGVtYWlsIGFkZHJlc3Mgb24gZmlsZSkuCgpXZSBjYW4gYWRkIHRoZSBlbWFpbCBkaW1lbnNpb24gdG8gdGhlIHBsb3QgaW4gRmlnLiA0LjIgYnkgY29sb3JpbmcgaW4gdGhlIHBvaW50cyBmb3IgY3VzdG9tZXJzIHdob3NlIGVtYWlsIGFkZHJlc3MgaXMga25vd24gdG8gdXMuIFRvIGRvIHRoaXMsIHdlIHVzZSBwbG90KCkgYXJndW1lbnRzIHRoYXQgYWxsb3cgdXMgdG8gZHJhdyBkaWZmZXJlbnQgY29sb3JzIChjb2w9KSBhbmQgc3ltYm9scyBmb3IgdGhlIHBvaW50cyAocGNoPSkuIEVhY2ggYXJndW1lbnQgdGFrZXMgYSB2ZWN0b3IgdGhhdCBzcGVjaWZpZXMgdGhlIG9wdGlvbuKAlHRoZSBjb2xvciBvciBzeW1ib2zigJR0aGF0IHlvdSB3YW50IGZvciBlYWNoIGluZGl2aWR1YWwgcG9pbnQuIFRodXMsIGlmIHdlIHByb3ZpZGUgYSB2ZWN0b3Igb2YgY29sb3JzIG9mIHRoZSBzYW1lIGxlbmd0aCBhcyB0aGUgdmVjdG9ycyBvZiB4IGFuZCB5IHZhbHVlcywgY29sPSB3aWxsIHVzZSB0aGUgY29ycmVzcG9uZGluZyBjb2xvcnMgZm9yIGVhY2ggcG9pbnQuIENvbnN0cnVjdGluZyBzdWNoIHZlY3RvcnMgY2FuIGJlIHRyaWNreSwgc28gd2Ugd2lsbCBidWlsZCB0aGVtIHVwIHNsb3dseS4KCgpUbyBiZWdpbiwgd2UgZmlyc3QgZGVjbGFyZSB2ZWN0b3JzIGZvciB0aGUgY29sb3IgYW5kIHBvaW50IHR5cGVzIHRoYXQgd2Ugd2FudCB0byB1c2U6CmBgYHtyIGhpc3RvZ3JhbSB3aXRoIGNvbG9yIGNvZGVkIHBvaW50cyBmb3IgZW1haWxlZCBvciBub3R9CgpteS5jb2wgPC0gYygiYmxhY2siLCAiZ3JlZW4zIikKbXkucGNoIDwtIGMoMSwgMTkpICMgUuKAmXMgc3ltYm9scyBmb3Igc29saWQgYW5kIG9wZW4gY2lyY2xlcyAoc2VlID9wb2ludHMpCm15LmNvbFthcy5udW1lcmljKGhlYWQoY3VzdC5kZiRlbWFpbCkpXQoKCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSBhIHZlY3RvciBvZiBjb2xvcnMsIHdlIGNhbiBwYXNzIGl0IGFzIHRoZSBjb2wgb3B0aW9uIGluIHBsb3QoKSB0byBnZXQgYSBwbG90IHdoZXJlIGN1c3RvbWVycyB3aXRoIGVtYWlscyBvbiBmaWxlIGFyZSBwbG90dGVkIGluIGdyZWVuIGFuZCBjdXN0b21lcnMgd2l0aG91dCBlbWFpbCBhZGRyZXNzZXMgb24gZmlsZSBhcmUgcGxvdHRlZCBpbiBibGFjay4gV2UgdXNlIGEgc2ltaWxhciBzdHJhdGVneSBmb3Igc2V0dGluZyB0aGUgcG9pbnQgc3R5bGVzIHVzaW5nIHRoZSBwY2ggb3B0aW9uLCBzdWNoIHRoYXQgY3VzdG9tZXJzIHdpdGhvdXQgZW1haWwgYWRkcmVzc2VzIGhhdmUgb3BlbiBjaXJjbGVzIGluc3RlYWQgb2Ygc29saWQuCgpgYGB7ciBub3cgdG8gcGxvdCB9CnBsb3QoY3VzdC5kZiRzdG9yZS5zcGVuZCwgY3VzdC5kZiRvbmxpbmUuc3BlbmQsCiBjZXg9MC43LAogY29sPW15LmNvbFtjdXN0LmRmJGVtYWlsXSwgcGNoPW15LnBjaFtjdXN0LmRmJGVtYWlsXSwKIG1haW49IkN1c3RvbWVycyBhcyBvZiBKdW5lIDIwMTQiLAogeGxhYj0iUHJpb3IgMTIgbW9udGhzIGluLXN0b3JlIHNhbGVzICgkKSIsCiB5bGFiPSJQcmlvciAxMiBtb250aHMgb25saW5lIHNhbGVzICgkKSIpCiMgYWRkIGEgbGVnZW5kIGJ5IHBhc3NpbmcgaW4gdGhlIGNvbCBhbmQgcGNoIHRoYXQgd2UgY3JlYXRlZCBlYXJsaWVyOikgCmxlZ2VuZCh4PSJ0b3ByaWdodCIsIGxlZ2VuZD1wYXN0ZSgiZW1haWwgb24gZmlsZToiLCBsZXZlbHMoY3VzdC5kZiRlbWFpbCkpLCBjb2w9bXkuY29sLCBwY2g9bXkucGNoKQpgYGAKCldoZW4gd2UgY3JlYXRlZCBGaWcuIDQuMSBlYXJsaWVyLCB3ZSB1c2VkIGFuIG9wdGlvbiBjb2w9ImJsdWUiIGFuZCBpdCB0dXJuZWQgYWxsIG9mIHRoZSBwb2ludHMgYmx1ZS4gVGhpcyBpcyBiZWNhdXNlIGlmIHRoZSB2ZWN0b3IgeW91IHBhc3MgZm9yIGNvbCBpcyBzaG9ydGVyIHRoYW4gdGhlIGxlbmd0aCBvZiB4IGFuZCB5LCB0aGVuIFIgcmVjeWNsZXMgdGhlIHZhbHVlcy4gVGh1cywgaWYgeW91ciBjb2wgdmVjdG9yIGhhcyBvbmUgZWxlbWVudCwgYWxsIHRoZSBwb2ludHMgd2lsbCBiZSB0aGF0IHNpbmdsZSBjb2xvci4gU2ltaWxhcmx5LCBpZiB5b3Ugd2VyZSB0byBwYXNzIHRoZXZlY3RvcmMoImJsYWNrIiwgImdyZWVuMyIpLHRoZW5wbG90d291bGRzaW1wbHltYWtlYWx0ZXJuYXRpbmcgcG9pbnRzIGJsYWNrIG9yIGdyZWVuLCB3aGljaCBtaWdodCBub3QgYmUgd2hhdCB5b3Ugd2FudC4gVXN1YWxseSB3aGF0IHlvdeKAmWxsIHdhbnQgaXMgdG8gY3JlYXRlIGEgdmVjdG9yIHRoYXQgZXhhY3RseSBtYXRjaGVzIHRoZSBsZW5ndGggb2YgeW91ciBkYXRhIGJ5IHN0YXJ0aW5nIHdpdGggYSBzaG9ydGVyIHZlY3RvciBhcyB3ZSBkaWQgaGVyZSwgYW5kIHRoZW4gaW5kZXhpbmcgaXQgd2l0aCBbXSBzdWNoIHRoYXQgeW91IGV4dHJhY3QgYSB2YWx1ZSBmb3IgZWFjaCBvbmUgb2YgeW91ciBkYXRhIHBvaW50cy4gVGhhdCBjYW4gYmUgZGlmZmljdWx0IHRvIGdldCByaWdodCBpbiBwcmFjdGljZSwgc28gd2UgZW5jb3VyYWdlIHlvdSB0byBleHBlcmltZW50IHdpdGggdGhlc2UgZXhhbXBsZXMgdW50aWwgeW91IHVuZGVyc3RhbmQgaG93IGl0IHdvcmtzLgoKCiMjIyA0LjIuMyBBZGRpbmcgYSBMZWdlbmQgdG8gYSBQbG90CkFib3ZlCgoKIyMjIDQuMi40IFBsb3R0aW5nIG9uIGEgTG9nIFNjYWxlCldpdGggcmF3IHZhbHVlcyBhcyBwbG90dGVkIGluIHRoZSBsZWZ0IHBhbmVsIG9mIEZpZy4gNC40LCBpdCBpcyBzdGlsbCBkaWZmaWN1bHQgdG8gc2VlIHdoZXRoZXIgdGhlcmUgaXMgYSBkaWZmZXJlbnQgcmVsYXRpb25zaGlwIGJldHdlZW4gaW4tc3RvcmUgYW5kIG9ubGluZSBwdXJjaGFzZXMgZm9yIHRob3NlIHdpdGggYW5kIHdpdGhvdXQgZW1haWxzIG9uIGZpbGUsIGJlY2F1c2Ugb2YgdGhlIGhlYXZ5IHNrZXcgaW4gc2FsZXMgZmlnLSB1cmVzLiBBIGNvbW1vbiBzb2x1dGlvbiBmb3Igc3VjaCBzY2F0dGVycGxvdHMgd2l0aCBza2V3ZWQgZGF0YSBpcyB0byBwbG90IHRoZSBkYXRhIG9uIGEgbG9nYXJpdGhtaWMgc2NhbGUuIFRoaXMgaXMgZWFzeSB0byBkbyB3aXRoIHRoZSBsb2c9IGFyZ3VtZW50IG9mIHBsb3QoKTogc2V0IGxvZz0ieCIgdG8gcGxvdCB0aGUgeC1heGlzIG9uIHRoZSBsb2cgc2NhbGUsIGxvZz0ieSIgZm9yIHRoZSB5LWF4aXMsIG9yIGxvZz0ieHkiIGZvciBib3RoIGF4ZXMuCgpTaW5jZSBib3RoIHZhbHVlcyBhcmUgc2tld2VkIHdlIHVzZSBsb2c9Inh5IiB0byBhcHBseSBsb2cgdG8gYm90aCB2YXJpYWJsZXMgCgpgYGB7ciB9CnBsb3QoY3VzdC5kZiRzdG9yZS5zcGVuZCArIDEsIGN1c3QuZGYkb25saW5lLnNwZW5kICsgMSwKIGxvZz0ieHkiLCBjZXg9MC43LAogY29sPW15LmNvbFtjdXN0LmRmJGVtYWlsXSwgcGNoPW15LnBjaFtjdXN0LmRmJGVtYWlsXSwKIG1haW49IkN1c3RvbWVycyBhcyBvZiBKdW5lIDIwMTQiLAogeGxhYj0iUHJpb3IgMTIgbW9udGhzIGluLXN0b3JlIHNhbGVzICgkKSIsCiB5bGFiPSJQcmlvciAxMiBtb250aHMgb25saW5lIHNhbGVzICgkKSIgKQogbGVnZW5kKHg9InRvcHJpZ2h0IiwgbGVnZW5kPXBhc3RlKCJlbWFpbCBvbiBmaWxlOiIsIGxldmVscyhjdXN0LmRmJGVtYWlsKSksCiBjb2w9bXkuY29sLCBwY2g9bXkucGNoKQoKIAogIyBJbiB0aGlzIGNvZGUsIHdlIHBsb3QgLi4ucyBwZW5kICsgMSB0byBhdm9pZCBhbiBlcnJvciBkdWUgdG8gdGhlIGZhY3QgdGhhdCBsb2coMCkgaXMgbm90IGRlZmluZWQuIEluIHRoZSByaWdodC1oYW5kIHNpZGUgb2YgRmlnLiA0LjQsIHRoZSBheGVzIGFyZSBub3cgbG9nYXJpdGhtaWM7IGZvciBpbnN0YW5jZSwgdGhlIGRpc3RhbmNlIGZyb20gMSB0byAxMCBpcyB0aGUgc2FtZSBhcyAxMOKAkzEwMC4KIApgYGAKClRodXMsIHRoZXJlIGlzIG5vIGV2aWRlbmNlIGhlcmUgdG8gc3VnZ2VzdCB0aGF0IG9ubGluZSBzYWxlcyBoYXZlIGNhbm5pYmFsaXplZCBpbi1zdG9yZSBzYWxlcyAoYSBmb3JtYWwgdGVzdCBvZiB0aGF0IHdvdWxkIGJlIGNvbXBsZXgsIGJ1dCB0aGUgcHJlc2VudCBkYXRhIGRvIG5vdCBhcmd1ZSBmb3Igc3VjaCBhbiBlZmZlY3QgaW4gYW55IG9idmlvdXMgd2F5KQoKCldlIGFsc28gc2VlIGluIEZpZy4gNC40IHRoYXQgY3VzdG9tZXJzIHdpdGggbm8gZW1haWwgYWRkcmVzcyBvbiBmaWxlIHNob3cgc2xpZ2h0bHkgbG93ZXIgb25saW5lIHNhbGVzIHRoYW4gdGhvc2Ugd2l0aCBhZGRyZXNzZXM7IHRoZXJlIGFyZSBzb21ld2hhdCBtb3JlIGJsYWNrIGNpcmNsZXMgaW4gdGhlIGxvd2VyIGhhbGYgb2YgdGhlIHBsb3QgdGhhbiB0aGUgdXBwZXIgaGFsZi4gSWYgd2UgaGF2ZSBiZWVuIHNlbmRpbmcgZW1haWwgcHJvbW90aW9ucyB0byBjdXN0b21lcnMsIHRoZW4gdGhpcyBzdWdnZXN0cyB0aGF0IHRoZSBwcm9tb3Rpb25zIG1pZ2h0IGJlIHdvcmtpbmcuIEFuIGV4cGVyaW1lbnQgdG8gY29uZmlybSB0aGF0IGh5cG90aGVzaXMgY291bGQgYmUgYW4gYXBwcm9wcmlhdGUgbmV4dCBzdGVwLgoKIyMjIDQuMyBDb21iaW5pbmcgUGxvdHMgaW4gYSBTaW5nbGUgR3JhcGhpY3MgT2JqZWN0CgpTb21ldGltZXMgd2Ugd2FudCB0byB2aXN1YWxpemUgc2V2ZXJhbCByZWxhdGlvbnNoaXBzIGF0IG9uY2UuIEZvciBpbnN0YW5jZSwgc3VwcG9zZSB3ZSB3aXNoIHRvIGV4YW1pbmUgd2hldGhlciBjdXN0b21lcnMgd2hvIGxpdmUgY2xvc2VyIHRvIHN0b3JlcyBzcGVuZCBtb3JlIGluIHN0b3JlLCBhbmQgd2hldGhlciB0aG9zZSB3aG8gbGl2ZSBmdXJ0aGVyIGF3YXkgc3BlbmQgbW9yZSBvbmxpbmUuIFRob3NlIGludm9sdmUgZGlmZmVyZW50IHNwZW5kaW5nIHZhcmlhYmxlcyBhbmQgdGh1cyBuZWVkIHNlcGFyYXRlIHBsb3RzLiBJZiB3ZSBwbG90IHNldmVyYWwgc3VjaCB0aGluZ3MgaW5kaXZpZHVhbGx5LCB3ZSBlbmQgdXAgd2l0aCBtYW55IGluZGl2aWR1YWwgY2hhcnRzLiBMdWNraWx5LCBSIGNhbiBwcm9kdWNlIGEgc2luZ2xlIGdyYXBoaWMgdGhhdCBjb25zaXN0cyBvZiBtdWx0aXBsZSBwbG90cy4gWW91IGRvIHRoaXMgYnkgdGVsbGluZyBSIHRoYXQgeW91IHdhbnQgbXVsdGlwbGUgcGxvdHMgaW4gYSBzaW5nbGUgZ3JhcGhpY2FsIG9iamVjdCB3aXRoIHRoZSBwYXIobWZyb3c9Li4uKSBjb21tYW5kOyB0aGVuIHNpbXBseSBwbG90IGVhY2ggb25lIHdpdGggcGxvdCgpIGFzIHVzdWFsLgoKYGBge3J9CnBhcihtZnJvdz1jKDIsIDIpKQpwbG90KGN1c3QuZGYkZGlzdGFuY2UudG8uc3RvcmUsIGN1c3QuZGYkc3RvcmUuc3BlbmQsIG1haW49InN0b3JlIikKcGxvdChjdXN0LmRmJGRpc3RhbmNlLnRvLnN0b3JlLCBjdXN0LmRmJG9ubGluZS5zcGVuZCwgbWFpbj0ib25saW5lIikKcGxvdChjdXN0LmRmJGRpc3RhbmNlLnRvLnN0b3JlLCBjdXN0LmRmJHN0b3JlLnNwZW5kKzEsIGxvZz0ieHkiLAogICAgIG1haW49InN0b3JlLCBsb2ciKQpwbG90KGN1c3QuZGYkZGlzdGFuY2UudG8uc3RvcmUsIGN1c3QuZGYkb25saW5lLnNwZW5kKzEsIGxvZz0ieHkiLAogICAgIG1haW49Im9ubGluZSwgbG9nIikKCmBgYAoKQWJvdmU6IHRoZSBncmFwaGljYWwgcGFyYW1ldGVyIG1mcm93IHRvIGMoMiwgMiksIHdoaWNoIGluc3RydWN0cyBSIHRvIGNyZWF0ZSBhIHNpbmdsZSBncmFwaGljIGNvbXByaXNpbmcgYSB0d28tYnktdHdvIGFycmFuZ2VtZW50IG9mIHBsb3RzLCB3aGljaCBiZWdpbnMgb24gdGhlIGZpcnN0IHJvdyBhbmQgbW92ZXMgZnJvbSBsZWZ0IHRvIHJpZ2h0LgoKCkFib3ZlOiBXZSBzZWUgYSBwb3NzaWJsZSBuZWdhdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBzcGVuZCBhbmQgZGlzdGFuY2UgdG8gc3RvcmUgCgojIyA0LjQgc2NhdHRlcnBsb3QgbWF0cmljZXMKIyMjIDQuNC4xIHBhaXJzKCkKCkluIG91ciBjdXN0b21lciBkYXRhLCB3ZSBoYXZlIGEgbnVtYmVyIG9mIHZhcmlhYmxlcyB0aGF0IG1pZ2h0IGJlIGFzc29jaWF0ZWQgd2l0aCBlYWNoIG90aGVyOyBhZ2UsIGRpc3RhbmNlLnRvLnN0b3JlLCBhbmQgZW1haWwgYWxsIG1pZ2h0IGJlIHJlbGF0ZWQgdG8gb24tIGxpbmUgYW5kIG9mZmxpbmUgdHJhbnNhY3Rpb25zIGFuZCB0byBzcGVuZGluZy4gV2hlbiB5b3UgaGF2ZSBzZXZlcmFsIHZhcmlhYmxlcyBzdWNoIGFzIHRoZXNlLCBpdCBpcyBnb29kIHByYWN0aWNlIHRvIGV4YW1pbmUgc2NhdHRlcnBsb3RzIGJldHdlZW4gYWxsIHBhaXJzIG9mIHZhcmlhYmxlcyBiZWZvcmUgbW92aW5nIG9uIHRvIG1vcmUgY29tcGxleCBhbmFseXNlcy4KCmBgYHtyIHBhaXJzKCkgfQoKcGFpcnMoZm9ybXVsYSA9IH4gYWdlICsgY3JlZGl0LnNjb3JlICsgZW1haWwgKwogIGRpc3RhbmNlLnRvLnN0b3JlICsgb25saW5lLnZpc2l0cyArIG9ubGluZS50cmFucyArCiAgb25saW5lLnNwZW5kICsgc3RvcmUudHJhbnMgKyBzdG9yZS5zcGVuZCwKICBkYXRhPWN1c3QuZGYpCgojc2FtZSBhcwoKcGFpcnMoY3VzdC5kZlsgLCBjKDI6MTApXSkgI291dHB1dCBub3QgcmVwZWF0ZWQ7IHNhbWUgYXMgYWJvdmUKYGBgCgoKZm9ybXVsYSA9IOKIvCBhZ2UgKyBjcmVkaXQuc2NvcmUgKyBlbWFpbCArIGRpc3RhbmNlLnRvLnN0b3JlICsgb25saW5lLnZpc2l0cyArIG9ubGluZS50cmFucyArIG9ubGluZS5zcGVuZCArIHN0b3JlLnRyYW5zICsgc3RvcmUuc3BlbmQsIGRhdGE9Y3VzdC5kZikKCgojIyMgNC40LjIgc2NhdHRlcnBsb3RNYXRyaXgoKQp0aGUgY2FyIHBhY2thZ2UgYWRkcyBzZXZlcmFsIGZlYXR1cmVzIHRvIHBhaXJzCgpgYGB7cn0KbGlicmFyeShjYXIpCgpzY2F0dGVycGxvdE1hdHJpeChmb3JtdWxhID0gfiBhZ2UgKyBjcmVkaXQuc2NvcmUgKyBlbWFpbCArIGRpc3RhbmNlLnRvLnN0b3JlICsgb25saW5lLnZpc2l0cyArIG9ubGluZS50cmFucyArIG9ubGluZS5zcGVuZCArIHN0b3JlLnRyYW5zICsgc3RvcmUuc3BlbmQsIGRhdGEgPSBjdXN0LmRmLCBkaWFnb25hbD0iaGlzdG9ncmFtIikKCmBgYAoKQSBsaW1pdGF0aW9uIG9mIEZpZ3MuIDQuNiBhbmQgNC43IGNvbmNlcm5zIHRoZSBkaXNwbGF5IG9mIHRoZSBlbWFpbCB2YXJpYWJsZS4gZW1haWwgaXMgYSBiaW5hcnkgZmFjdG9yIHdpdGggdmFsdWVzIHllcyBhbmQgbm8sIGFuZCBhIHNjYXR0ZXJwbG90IGlzIG5vdCBpZGVhbCB0byB2aS0gc3VhbGl6ZSBhIGRpc2NyZXRlIHZhcmlhYmxlLiBGb3Igc3VjaCB2YXJpYWJsZXMsIHRoZSBncGFpcnMsIG9yIEdlbmVyYWxpemVkIFBhaXIgUGxvdHMsIHBhY2thZ2UgWzQxXSBwcm92aWRlcyBhIGZ1bmN0aW9uIGNhbGxlZCBncGFpcnMoKSB0aGF0IHByb2R1Y2VzIGEgc2NhdC0gdGVycGxvdCBtYXRyaXggdGhhdCBpbmNsdWRlcyBiZXR0ZXIgdmlzdWFsaXphdGlvbnMgZm9yIGJvdGggZGlzY3JldGUgYW5kIGNvbnRpbnVvdXMgdmFyaWFibGVzLiBGb3IgZXhhbXBsZSwgaWYgd2Ugd2FudCB0byBsb29rIG1vcmUgY2xvc2VseSBhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZW1haWwgYW5kIG9ubGluZS52aXNpdHMsIG9ubGluZS50cmFucyBhbmQgb25saW5lLnNwZW5kLCB3ZSBjYW4gdXNlIGdwYWlycygpIGFzIGZvbGxvd3M6CgpgYGB7ciBnZ3BhaXJzIHRvIGhhbmRsZSBiaW5hcnkgYW5kIGRpc2NyZXRlIGRhdGF9CgoKIGxpYnJhcnkoZ3BhaXJzKQogZ3BhaXJzKGN1c3QuZGZbICwgYygyOjEwKV0pCiAjIGRvZXMgbm90IGFjY2VwdCBmb3JtdWxhIGlucHV0CgpgYGAKZ3BhaXJzKCkgcHJvZHVjZXMgKnNjYXR0ZXJwbG90cyBmb3IgcGFpcnMgb2YgY29udGludW91cyB2YXJpYWJsZXMqLiBIb3dldmVyLCBmb3IgdGhlIGZhY3RvciBlbWFpbCwgKmdwYWlycyBpbmNsdWRlcyBhIGJveHBsb3QgdGhhdCBjb21wYXJlcyB0aGUgZGlzdHJpYnV0aW9uIG9mIGNvbnRpbnVvdXMgdmFyaWFibGVzIGZvciB0aG9zZSB3aG8gZG8gYW5kIGRvIG5vdCBoYXZlIGVtYWlsIGFkZHJlc3NlcyBpbiB0aGUgZGF0YS4qICBBICpib3hwbG90IHNob3dzIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgdmlzaXRzLCB0cmFuc2FjdGlvbnMsIGFuZCBzcGVuZGluZyBoYXZlIGxvbmdlciB0YWlscyBhbW9uZyBjdXN0b21lcnMgd2hvIGhhdmUgZW1haWwgYWRkcmVzc2VzIG9uIGZpbGUgdGhhbiB0aG9zZSB3aG8gZG9u4oCZdCouIFdlIGRpc2N1c3MgYm94cGxvdHMgaW4gZGVwdGggaW4gQ2hhcC4gNSwgd2hpY2ggZm9jdXNlcyBvbiBjb21wYXJpc29ucyBiZXR3ZWVuIGdyb3Vwcy4KCmJlY2F1c2UgaXQgc2VsZWN0cyBpbmRpdmlkdWFsIHBsb3RzIHRvIGZpdCB0aGUgZGF0YSB0eXBlcywgZ3BhaXJzKCkgaXMgdXNlZnVsIGZvciBtYXItIGtldGluZyBkYXRhIHNldHMgdGhhdCBpbmNsdWRlIGNvbnRpbnVvdXMgYW5kIGRpc2NyZXRlIHZhcmlhYmxlcy4gTm90ZSB0aGF0IGdwYWlycygpIHJlbGllcyBvbiB0aGUgZGF0YSB0eXBlcyBpbiBSIHRvIGRldGVybWluZSBob3cgdG8gY29uc3RydWN0IGl0cyBwbG90czsgaWYgd2UgaGFkIHN0b3JlZAoKIyMgNC41IENvcnJlbGF0aW9uIENvZWZmaWNpZW50cyAKYXNzZXNzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBlYWNoIHBhaXIgd2l0aCBhIHNpbmdsZSBudW1iZXIuIE9uZSBtZWFzdXJlIG9mIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0d28gdmFyaWFibGVzIGlzIHRoZSBjb3ZhcmktIGFuY2UsIHdoaWNoIGNhbiBiZSBjb21wdXRlZCBmb3IgYW55IHR3byB2YXJpYWJsZXMgdXNpbmcgdGhlIGNvdiBmdW5jdGlvbjoKCgpgYGB7ciBjb3Z9Cgpjb3YoY3VzdC5kZiRhZ2UsIGN1c3QuZGYkY3JlZGl0LnNjb3JlKQoKYGBgCgpIb3dldmVyLCBpdCBpcyBkaWZmaWN1bHQgdG8gaW50ZXJwcmV0IHRoZSBtYWduaXR1ZGUgb2YgY292YXJpYW5jZSBiZWNhdXNlIHRoZSBzY2FsZSBkZXBlbmRzIG9uIHRoZSB2YXJpYWJsZXMgaW52b2x2ZWQuICpDb3ZhcmlhbmNlIHdpbGwgYmUgZGlmZmVyZW50IGlmIHRoZSB2YXJpYWJsZXMgYXJlIG1lYXN1cmVkIGluIGNlbnRzIHZlcnN1cyBkb2xsYXJzIG9yIGluIGluY2hlcyB2ZXJzdXMgY2VudGltZXRlcnMuKiBTbywgaXQgaXMgaGVscGZ1bCB0byBzY2FsZSB0aGUgY292YXJpYW5jZSBieSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIGZvciBlYWNoIHZhcmlhYmxlLCB3aGljaCByZXN1bHRzIGluIGEgc3RhbmRhcmRpemVkLCByZXNjYWxlZCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBrbm93biBhcyB0aGUgUGVhcnNvbiBwcm9kdWN0LW1vbWVudCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCwgb2Z0ZW4gYWJicmV2aWF0ZWQgYXMgdGhlIHN5bWJvbCByLgoKYGBge3IgY29yfQoKY29yKGN1c3QuZGYkYWdlLCBjdXN0LmRmJGNyZWRpdC5zY29yZSkKYGBgCnIgaXMgaWRlbnRpY2FsIHRvIHJlc2NhbGUgdGhlIGNvdmFyaWFuY2UgYnkgdGhlIGpvaW4gc3RkLiBkZXYgKGJ1dCBtb3JlIGNvbnYpCmBgYHtyIGNvcnJ9CmNvdihjdXN0LmRmJGFnZSwgY3VzdC5kZiRjcmVkaXQuc2NvcmUpIC8gKHNkKGN1c3QuZGYkYWdlKSpzZChjdXN0LmRmJGNyZWRpdC5zY29yZSkpCgpgYGAKCldoYXQgdmFsdWUgb2YgciBzaWduaWZpZXMgYW4gaW1wb3J0YW50IGNvcnJlbGF0aW9uIGJldHdlZW4gdHdvIHZhcmlhYmxlcyBpbiBtYXItIGtldGluZz8gSW4gZW5naW5lZXJpbmcgYW5kIHBoeXNpY2FsIHNjaWVuY2VzLCBwaHlzaWNhbCBtZWFzdXJlbWVudHMgbWF5IGRlbW9uLSBzdHJhdGUgZXh0cmVtZWx5IGhpZ2ggY29ycmVsYXRpb25zOyBmb3IgaW5zdGFuY2UsIHIgYmV0d2VlbiB0aGUgbGVuZ3RocyBhbmQgd2VpZ2h0cyBvZiBwaWVjZXMgb2Ygc3RlZWwgcm9kIG1pZ2h0IGJlIDAuOSwgMC45NSwgb3IgZXZlbiAwLjk5OSwgZGVwZW5kaW5nIG9uIHRoZSB1bmlmb3ItIG1pdHkgb2YgdGhlIHJvZHMgYW5kIHRoZSBwcmVjaXNpb24gb2YgbWVhc3VyZW1lbnQuIEhvd2V2ZXIsIGluIHNvY2lhbCBzY2llbmNlcyBzdWNoIGFzIG1hcmtldGluZywgd2UgYXJlIGNvbmNlcm5lZCB3aXRoIGh1bWFuIGJlaGF2aW9yLCB3aGljaCBpcyBsZXNzIGNvbnNpc3RlbnQgYW5kIG1vcmUgZGlmZmljdWx0IHRvIG1lYXN1cmUuIFRoaXMgcmVzdWx0cyBpbiBsb3dlciBjb3JyZWxhdGlvbnMsIGJ1dCB0aGV5IGFyZSBzdGlsbCBpbXBvcnRhbnQuCgpXZSBvZnRlbiB1c2UgQ29oZW7igJlzIFJ1bGVzIG9mIFRodW1iLCB3aGljaCBjb21lIG91dCBvZiB0aGUgcHN5Y2hvbG9neSB0cmFkaS0gdGlvbiBbMjddLiBDb2hlbiBwcm9wb3NlZCB0aGF0IGZvciBjb3JyZWxhdGlvbnMgYmV0d2VlbiB2YXJpYWJsZXMgZGVzY3JpYmluZyBwZW8tIHBsZSwgciA9IDAuMSBzaG91bGQgYmUgY29uc2lkZXJlZCBhIHNtYWxsIG9yIHdlYWsgYXNzb2NpYXRpb24sIHIgPSAwLjMgbWlnaHQgYmUgY29uc2lkZXJlZCB0byBiZSBtZWRpdW0gaW4gc3RyZW5ndGgsIGFuZCByID0gMC41IG9yIGhpZ2hlciBjb3VsZCBiZSBjb25zaWRlcmVkIHRvIGJlIGxhcmdlIG9yIHN0cm9uZy4gQ29oZW7igJlzIGludGVycHJldGF0aW9uIG9mIGEgbGFyZ2UgZWZmZWN0IHdhcyB0aGF0IHN1Y2ggYW4gYXNzby0gY2lhdGlvbiB3b3VsZCBiZSBlYXNpbHkgbm90aWNlZCBieSBjYXN1YWwgb2JzZXJ2ZXJzLiBBIHNtYWxsIGVmZmVjdCB3b3VsZCByZXF1aXJlIGNhcmVmdWwgbWVhc3VyZW1lbnQgdG8gZGV0ZWN0IHlldCBtaWdodCBiZSBpbXBvcnRhbnQgdG8gb3VyIHVuZGVyc3RhbmRpbmcgYW5kIHRvIHN0YXRpc3RpY2FsIG1vZGVscy4KCipJbXBvcnRhbnRseSwgaW50ZXJwcmV0YXRpb24gb2YgciBhY2NvcmRpbmcgdG8gQ29oZW7igJlzIHJ1bGVzIG9mIHRodW1iIGRlcGVuZHMgb24gdGhlIGFzc3VtcHRpb24gdGhhdCB0aGUgdmFyaWFibGVzIGFyZSBub3JtYWxseSBkaXN0cmlidXRlZCAoYWxzbyBrbm93biBhcyBHYXVzc2lhbikgb3IgYXJlIGFwcHJveGltYXRlbHkgc28qCgoKIyMjIDQuNS4xIENvcnJlbGF0aW9uIFRlc3RzCklzIGl0IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQ/CmBgYHtyIGNvciB0ZXN0fQoKY29yLnRlc3QoY3VzdC5kZiRhZ2UsIGN1c3QuZGYkY3JlZGl0LnNjb3JlKQoKYGBgCgpBYm92ZTogVGhpcyB0ZWxscyB1cyB0aGF0IHIgPSAwLjI1IGFuZCB0aGUgOTUgJSBjb25maWRlbmNlIGludGVydmFsIGlzIHIgPSAwLjE5NiDiiJIgMC4zMTIuIEJlY2F1c2UgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIHIgZG9lcyBub3QgaW5jbHVkZSAwIChhbmQgdGh1cyBoYXMgcC12YWx1ZSBvZiBwIDwgMC4wNSksIHRoZSBhc3NvY2lhdGlvbiBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LiBTdWNoIGEgY29ycmVsYXRpb24sIHNob3dpbmcgYSBtZWRpdW0tc2l6ZWQgZWZmZWN0IGFuZCBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UsIHByb2JhYmx5IHNob3VsZCBub3QgYmUgaWdub3JlZCBpbiBzdWJzZXF1ZW50IGFuYWx5c2VzLgoKCiMjIyA0LjUuMiBDb3JyZWxhdGlvbiBNYXRyaWNlcwpmb3IgbW9yZSB0aGFuIHR3byB2YXJzIHlvdSBjYW4gY29tcHV0ZSB0aGUgY29ycnMgYmV0d2VlbiBhbGwgcGFpcnMgeCx5CmBgYHtyIGNvcnIgbWF0cmljZXN9CmxpYnJhcnkoZHBseXIpCmNvcnJtYXQgPC0gZGF0YS5mcmFtZShjb3IoY3VzdC5kZlssIGMoMiwgMywgNSwgNiw3LDgsOSwxMCldKSkKCgpjb3JybWF0W2NvcnJtYXQgPiAuMjBdCmZpbHRlcihjb3JybWF0LCBmdW5jdGlvbih4KXsgIGlmZWxzZSh4ID4gLjIwLCBUUlVFLCBGQUxTRSl9KQoKYGBgCgpgYGB7ciBjb3JycGxvdHN9CgpsaWJyYXJ5KGNvcnJwbG90KSAgICAjIGZvciBjb3JyZWxhdGlvbiBwbG90LCBpbnN0YWxsIGlmIG5lZWRlZApsaWJyYXJ5KGdwbG90cykgICAgICAjIGNvbG9yIGludGVycG9sYXRpb24sIGluc3RhbGwgaWYgbmVlZGVkCmNvcnJwbG90Lm1peGVkKGNvcnI9Y29yKGN1c3QuZGZbICwgYygyLCAzLCA1OjEwKV0sIHVzZT0iY29tcGxldGUub2JzIiksCiAgICAgICAgICAgICAgIHVwcGVyPSJlbGxpcHNlIiwgdGwucG9zPSJsdCIsCiAgICAgICAgICAgICAgIGNvbCA9IGNvbG9ycGFuZWwoNTAsICJyZWQiLCAiZ3JheTYwIiwgImJsdWU0IikpCgpgYGAKCm51bWVyaWMgdmFsdWVzIG9mIHIgYXJlIHNob3duIGluIHRoZSBsb3dlciB0cmlhbmdsZSBvZiB0aGUgbWF0cml4LiBUaGUgdXBwZXIgdHJpYW5nbGUgZGlzcGxheXMgZWxsaXBzZXMgKGJlY2F1c2Ugd2UgdXNlZCB0aGUgYXJndW1lbnQgdXBwZXI9ImVsbGlwc2UiKS4gVGhlc2UgZWxsaXBzZXMgYXJlIHRpZ2h0ZXIsIHByb2dyZXNzaXZlbHkgY2xvc2VyIHRvIGJlaW5nIGxpbmVzLCBmb3IgbGFyZ2VyIHZhbHVlcyBvZiByLCBhbmQgYXJlIHJvdW5kZXIsIG1vcmUgbGlrZSBjaXJjbGVzIGZvciByIG5lYXIgemVyby4gVGhleSBhcmUgYWxzbyBzaGFkZWQgYmx1ZSBmb3IgcG9zaXRpdmUgZGlyZWN0aW9uLCBhbmQgcmVkIGZvciBuZWdhdGl2ZSAoYW5kIHNob3cgY29ycmVzcG9uZGluZyBwb3NpdGl2ZSBvciBuZWdhdGl2ZSAKClRoaXMgbWFrZXMgaXQgZWFzeSB0byBmaW5kIHRoZSBsYXJnZXIgY29ycmVsYXRpb25zIGluIHRoZSBkYXRhOiBhZ2UgaXMgcG9zaXRpdmVseSBjb3ItIHJlbGF0ZWQgd2l0aCBjcmVkaXQuc2NvcmU7IGRpc3RhbmNlLnRvLnN0b3JlIGlzIG5lZ2F0aXZlbHkgY29ycmVsYXRlZCB3aXRoIHN0b3JlLnRyYW5zIGFuZCBzdG9yZS5zcGVuZDsgb25saW5lLnZpc2l0cywgb25saW5lLnRyYW5zLCBhbmQgb25saW5lLnNwZW5kIGFyZSBhbGwgc3Ryb25nbHkgY29ycmVsYXRlZCB3aXRoIG9uZSBhbm90aGVyLCBhcyBhcmUgc3RvcmUuIHRyYW5zIGFuZCBzdG9yZS5zcGVuZC4gSW4gdGhlIHN1cnZleSBpdGVtcywgc2F0LnNlcnZpY2UgaXMgcG9zaXRpdmVseSBjb3ItIHJlbGF0ZWQgd2l0aCBzYXQuc2VsZWN0aW9uLgoKVGhpcyBtYWtlcyBpdCBlYXN5IHRvIGZpbmQgdGhlIGxhcmdlciBjb3JyZWxhdGlvbnMgaW4gdGhlIGRhdGE6IGFnZSBpcyBwb3NpdGl2ZWx5IGNvci0gcmVsYXRlZCB3aXRoIGNyZWRpdC5zY29yZTsgZGlzdGFuY2UudG8uc3RvcmUgaXMgbmVnYXRpdmVseSBjb3JyZWxhdGVkIHdpdGggc3RvcmUudHJhbnMgYW5kIHN0b3JlLnNwZW5kOyBvbmxpbmUudmlzaXRzLCBvbmxpbmUudHJhbnMsIGFuZCBvbmxpbmUuc3BlbmQgYXJlIGFsbCBzdHJvbmdseSBjb3JyZWxhdGVkIHdpdGggb25lIGFub3RoZXIsIGFzIGFyZSBzdG9yZS4gdHJhbnMgYW5kIHN0b3JlLnNwZW5kLiBJbiB0aGUgc3VydmV5IGl0ZW1zLCBzYXQuc2VydmljZSBpcyBwb3NpdGl2ZWx5IGNvci0gcmVsYXRlZCB3aXRoIHNhdC5zZWxlY3Rpb24uCgoKIyMjIDQuNS4zIFRyYW5zZm9ybWluZyBWYXJpYWJsZXMgYmVmb3JlIENvbXB1dGluZyBDb3JyZWxhdGlvbnMKCk5vdGljZSB3ZSBhcmUgY29tcGFyaW5nIHggeCYyIHRvIHNob3cgdGhlIGxhY2sgb2YgImxpbmVhciIgY29ycmVsYXRpb24gdGhvdWdoIHdlIGtub3cgdGhleSBhcmUgcmVsYXRlZApgYGB7cn0KI05vdGljZSB3ZSBhcmUgY29tcGFyaW5nIHggeCYyIHRvIHNob3cgdGhlIGxhY2sgb2YgImxpbmVhciIgY29ycmVsYXRpb24gdGhvdWdoIHdlIGtub3cgdGhleSBhcmUgcmVsYXRlZApzZXQuc2VlZCg0OTkzMSkKeCA8LSBydW5pZigxMDAwLCBtaW49LTEwLCBtYXg9MTApCgpwbG90KHgsIHheMikKY29yKHgsIHheMikKCgpgYGAKCkFib3ZlOiBNYW55IHJlbGF0aW9uc2hpcHMgaW4gbWFya2V0aW5nIGRhdGEgYXJlIG5vbmxpbmVhci4gVGhlIG51bWJlciBvZiB0cmlwcyBhIGN1c3RvbWVyIG1ha2VzIHRvIGEgc3RvcmUgbWF5IGJlIGludmVyc2VseSByZWxhdGVkIHRvIGRpc3RhbmNlIGZyb20gdGhlIHN0b3JlLiAKCmBgYHtyfQpjb3IoY3VzdC5kZiRkaXN0YW5jZS50by5zdG9yZSwgY3VzdC5kZiRzdG9yZS5zcGVuZCkKCmBgYAoKCg==