Packages

library(readr)
library(tidyverse)
library(tidymodels)
library(psych)
library(caret)
library(rpart)
library(rpart.plot)
library(corrplot)
library(RColorBrewer)
library(labelled)
library(ggplot2)
library(ggforce)
library(kableExtra)
library(gridExtra)
library(Metrics)

Instructions

Exploratory analysis and essay

Pre-work

  1. Visit the following website and explore the range of sizes of this dataset (from 100 to 5 million records): https://excelbianalytics.com/wp/downloads-18-sample-csv-files-data-sets-for-testing-sales/ or (new) https://www.kaggle.com/datasets
  2. Select 2 files to download Based on your computer’s capabilities (memory, CPU), select 2 files you can handle (recommended one small, one large)
  3. Download the files
  4. Review the structure and content of the tables, and think about the data sets (structure, size, dependencies, labels, etc)
  5. Consider the similarities and differences in the two data sets you have downloaded
  6. Think about how to analyze and predict an outcome based on the datasets available
  7. Based on the data you have, think which two machine learning algorithms presented so far could be used to analyze the data

Deliverable

  1. Essay (minimum 500 word document) Write a short essay explaining your selection of algorithms and how they relate to the data and what you are trying to do
  2. Exploratory Analysis (EDA) using R or Python (submit code + errors + analysis as notebook or copy/paste to document) Explore how to analyze and predict an outcome based on the data available. This will be an exploratory exercise, so feel free to show errors and warnings that raise during the analysis. Test the code with both datasets selected and compare the results.

Answer questions such as:

  1. Are the columns of your data correlated?
  2. Are there labels in your data? Did that impact your choice of algorithm?
  3. What are the pros and cons of each algorithm you selected?
  4. How your choice of algorithm relates to the datasets (was your choice of algorithm impacted by the datasets you chose)?
  5. Which result will you trust if you need to make a business decision?
  6. Do you think an analysis could be prone to errors when using too much data, or when using the least amount possible?
  7. How does the analysis between data sets compare?

Develop your exploratory analysis of the data and the essay in the following 2 weeks.

DATA

url<-"https://raw.githubusercontent.com/GitableGabe/Data624_Data/main/"
df_1k <- read.csv(paste0(url,"1000%20Sales%20Records.csv"))
df_100k <- read.csv(paste0(url,"100000%20Sales%20Records.csv"))

EDA

Familiarization with Sales datasets extracted from excelbi analytics requires understanding of dataset composition, dimensions, column types, NA or Null value count, etc.

Data Composition

str(df_1k)
'data.frame':   1000 obs. of  14 variables:
 $ Region        : chr  "Middle East and North Africa" "North America" "Middle East and North Africa" "Asia" ...
 $ Country       : chr  "Libya" "Canada" "Libya" "Japan" ...
 $ Item.Type     : chr  "Cosmetics" "Vegetables" "Baby Food" "Cereal" ...
 $ Sales.Channel : chr  "Offline" "Online" "Offline" "Offline" ...
 $ Order.Priority: chr  "M" "M" "C" "C" ...
 $ Order.Date    : chr  "10/18/2014" "11/7/2011" "10/31/2016" "4/10/2010" ...
 $ Order.ID      : int  686800706 185941302 246222341 161442649 645713555 683458888 679414975 208630645 266467225 118598544 ...
 $ Ship.Date     : chr  "10/31/2014" "12/8/2011" "12/9/2016" "5/12/2010" ...
 $ Units.Sold    : int  8446 3018 1517 3322 9845 9528 2844 7299 2428 4800 ...
 $ Unit.Price    : num  437.2 154.06 255.28 205.7 9.33 ...
 $ Unit.Cost     : num  263.33 90.93 159.42 117.11 6.92 ...
 $ Total.Revenue : num  3692591 464953 387260 683335 91854 ...
 $ Total.Cost    : num  2224085 274427 241840 389039 68127 ...
 $ Total.Profit  : num  1468506 190526 145420 294296 23726 ...
str(df_100k)
'data.frame':   100000 obs. of  14 variables:
 $ Region        : chr  "Middle East and North Africa" "Central America and the Caribbean" "Sub-Saharan Africa" "Sub-Saharan Africa" ...
 $ Country       : chr  "Azerbaijan" "Panama" "Sao Tome and Principe" "Sao Tome and Principe" ...
 $ Item.Type     : chr  "Snacks" "Cosmetics" "Fruits" "Personal Care" ...
 $ Sales.Channel : chr  "Online" "Offline" "Offline" "Online" ...
 $ Order.Priority: chr  "C" "L" "M" "M" ...
 $ Order.Date    : chr  "10/8/2014" "2/22/2015" "12/9/2015" "9/17/2014" ...
 $ Order.ID      : int  535113847 874708545 854349935 892836844 129280602 473105037 754046475 772153747 847788178 471623599 ...
 $ Ship.Date     : chr  "10/23/2014" "2/27/2015" "1/18/2016" "10/12/2014" ...
 $ Units.Sold    : int  934 4551 9986 9118 5858 1149 7964 6307 8217 2758 ...
 $ Unit.Price    : num  152.58 437.2 9.33 81.73 668.27 ...
 $ Unit.Cost     : num  97.44 263.33 6.92 56.67 502.54 ...
 $ Total.Revenue : num  142510 1989697 93169 745214 3914726 ...
 $ Total.Cost    : num  91009 1198415 69103 516717 2943879 ...
 $ Total.Profit  : num  51501 791282 24066 228497 970846 ...
kable(as.data.frame(table(df_1k$Region)) %>% arrange(desc(Freq)),
      caption = "Frequency Region df_1k")
Frequency Region df_1k
Var1 Freq
Europe 267
Sub-Saharan Africa 262
Middle East and North Africa 138
Asia 136
Central America and the Caribbean 99
Australia and Oceania 79
North America 19
kable(as.data.frame(table(df_100k$Region)) %>% arrange(desc(Freq)),
      caption = "Frequency Region df_100k")
Frequency Region df_100k
Var1 Freq
Sub-Saharan Africa 26019
Europe 25877
Asia 14547
Middle East and North Africa 12580
Central America and the Caribbean 10731
Australia and Oceania 8113
North America 2133
kable(as.data.frame(table(df_1k$Item.Type )) %>% arrange(desc(Freq)),
      caption = "Frequency Item.Type df_1k")
Frequency Item.Type df_1k
Var1 Freq
Beverages 101
Vegetables 97
Office Supplies 89
Baby Food 87
Personal Care 87
Snacks 82
Cereal 79
Clothes 78
Meat 78
Household 77
Cosmetics 75
Fruits 70
kable(as.data.frame(table(df_100k$Item.Type )) %>% arrange(desc(Freq)),
            caption = "Frequency Item Type 100k")
Frequency Item Type 100k
Var1 Freq
Office Supplies 8426
Cereal 8421
Baby Food 8407
Cosmetics 8370
Personal Care 8364
Meat 8320
Snacks 8308
Clothes 8304
Vegetables 8282
Household 8278
Fruits 8262
Beverages 8258
kable(as.data.frame(table(df_1k$Sales.Channel )) %>% arrange(desc(Freq)),
      caption = "Frequency Sales Channel 1k")
Frequency Sales Channel 1k
Var1 Freq
Offline 520
Online 480
kable(as.data.frame(table(df_100k$Sales.Channel )) %>% arrange(desc(Freq)),
      caption = "Frequency Sales Channel 100k")
Frequency Sales Channel 100k
Var1 Freq
Online 50054
Offline 49946
var_label(df_1k)
$Region
NULL

$Country
NULL

$Item.Type
NULL

$Sales.Channel
NULL

$Order.Priority
NULL

$Order.Date
NULL

$Order.ID
NULL

$Ship.Date
NULL

$Units.Sold
NULL

$Unit.Price
NULL

$Unit.Cost
NULL

$Total.Revenue
NULL

$Total.Cost
NULL

$Total.Profit
NULL
var_label(df_100k)
$Region
NULL

$Country
NULL

$Item.Type
NULL

$Sales.Channel
NULL

$Order.Priority
NULL

$Order.Date
NULL

$Order.ID
NULL

$Ship.Date
NULL

$Units.Sold
NULL

$Unit.Price
NULL

$Unit.Cost
NULL

$Total.Revenue
NULL

$Total.Cost
NULL

$Total.Profit
NULL
# Dimensions
dim_1k_tmp<-dim(df_1k)
dim_100k_tmp<-dim(df_100k)
# Class
class_1k_tmp<-sapply(df_1k,class)
class_100k_tmp<-sapply(df_100k,class)



column_name_1k_tmp <- "Order.ID"

# Count the number of duplicates in the specified column
num_duplicates_1k_tmp <- sum(duplicated(df_1k[[column_name_1k_tmp]]) |
                            duplicated(df_1k[[column_name_1k_tmp]],
                                       fromLast = TRUE))

column_name_100k_tmp <- "Order.ID"

# Count the number of duplicates in the specified column
num_duplicates_100k_tmp <- sum(duplicated(df_100k[[column_name_100k_tmp]]) |
                            duplicated(df_100k[[column_name_100k_tmp]],
                                       fromLast = TRUE))


na_null_cnt_tmp<-(sum(colSums(is.na(df_1k) | is.null(df_1k)))+
                    sum(colSums(is.na(df_100k) | is.null(df_100k))))

region_tmp<-unique(df_1k$Region)
country_len_tmp<-length(unique(df_1k$Country))

The dataset of size 1000 is stored to df_1k and the dataset size 100,000 is stored to df_100k

  • df_1k dimensions is 1000 rows and 14 columns.
  • df_100k dimensions is 100000 rows and 14 columns.
  • The column types for df_1k are character, character, character, character, character, character, integer, character, integer, numeric, numeric, numeric, numeric, numeric
  • The column types for df_100k are character, character, character, character, character, character, integer, character, integer, numeric, numeric, numeric, numeric, numeric
  • Notable categories include
    • Order.Date and Ship.Date the only date valued columns, but set to type chr and may need converting.
    • Order.ID is compose of unique values with 0 duplicates found in the df_1k data and 0 found in the df_100k data.
    • Region and Country both of which define location
    • Item.Type for type of item sold.
    • Sales.Channel defines sales method as an online or offline purchase, or e-purchase vs in-store.
    • Order.Priority which has a ranking of severity.
    • Attributes labeled with Total that are calculated values.
    • Using the length() functions we see that 185 countries are listed in the data.
  • Using the table function we see: -Of the Regions listed Sub-Saharan Africa and Europe is most frequented.
    • For df_1k Beverages and Vegetables is most frequented, however with df_100k Office Supplies and Cereals is.
    • For df_1k more purchases are done Offline while for df_100k more is done Online Albiet by a small margin in both cases.

With respect to dependencies, the formulas below highlight the dependency that exists with calculated variables with the label Total in there Attribute name.

\(Total.Cost=Units.Sold\times Unit.Cost\) making Total.Cost dependent on Units.Sold and Unit Cost \(Total.Revenue=Units.Sold\times Unit.Price\) making Total.Revenue dependent on Units.Sold and Unit.Price \(Total.Profit=Total.Revenue-Total.Cost\) making the subsequent totals above the dependent variables for Total.Profit

The Order.Priority have a dependency based on ranking of M, C, H, L Which is Critical, High, Medium, Low in ascending order.

Date values are dependent in interpretation, with calculation of Order.Date and Ship.Date being a factor of performance or timeliness.

Data Tranformation

df_1k[['Order.Date']] <- as.Date(df_1k[['Order.Date']], "%m/%d/%Y")
df_1k[['Ship.Date']] <- as.Date(df_1k[['Ship.Date']], "%m/%d/%Y")

df_100k[['Order.Date']] <- as.Date(df_100k[['Order.Date']], "%m/%d/%Y")
df_100k[['Ship.Date']] <- as.Date(df_100k[['Ship.Date']], "%m/%d/%Y")

df_1k$Order.Priority <- as.factor(df_1k$Order.Priority)
df_100k$Order.Priority <- as.factor(df_100k$Order.Priority)

The most obvious transformations were the date values as noted in EDA and factoring the categories in Order.Priority

df_1k$Sales.Channel <- as.factor(df_1k$Sales.Channel)
df_100k$Sales.Channel <- as.factor(df_100k$Sales.Channel)
df_1k$Item.Type <- as.factor(df_1k$Item.Type)
df_100k$Item.Type <- as.factor(df_100k$Item.Type)
df_1k$Region <- as.factor(df_1k$Region)
df_100k$Region <- as.factor(df_100k$Region)
df_1k$Country <- as.factor(df_1k$Country)
df_100k$Country <- as.factor(df_100k$Country)

Sales.Channel,Item.Type and Region were also logical choices, considering the amount of unique values for Country and the nature of its relationship with Region, I believe if I make a model with Region, Country would be excluded. Order.ID are just arbitrary, chronological or incremented numbers therefor it was not set as a factor.

levels(df_1k$Region)
[1] "Asia"                              "Australia and Oceania"             "Central America and the Caribbean" "Europe"                            "Middle East and North Africa"     
[6] "North America"                     "Sub-Saharan Africa"               

Correlation and Skewness

describe(df_1k%>%
           dplyr::select(contains("Unit") | contains("Total"))) %>% 
  dplyr::select(c(mean,sd,min,max,range,se,skew))
describe(df_100k%>%
           dplyr::select(contains("Unit") | contains("Total"))) %>% 
  dplyr::select(c(mean,sd,min,max,range,se,skew))
plot_numeric_1k<-df_1k%>%
      dplyr::select(contains("Unit") | contains("Total")) %>%
  gather(variable, value, 1:6) %>%
  ggplot(aes(value)) +
    facet_wrap(~variable, scales = "free") +
    geom_density(fill = "lightgreen", alpha=0.9, color="lightgreen") +
    geom_histogram(aes(y=after_stat(density)), alpha=0.2, fill = "lightblue",
                   color="darkred", position="identity", bins = 40)

plot_numeric_100k<-df_100k%>%
      dplyr::select(contains("Unit") | contains("Total")) %>%
  gather(variable, value, 1:6) %>%
  ggplot(aes(value)) +
    facet_wrap(~variable, scales = "free") +
    geom_density(fill = "lightgreen", alpha=0.9, color="lightgreen") +
    geom_histogram(aes(y=after_stat(density)), alpha=0.2, fill = "pink",
                   color="darkred", position="identity", bins = 40)

grid.arrange(plot_numeric_1k,plot_numeric_100k,ncol=1)

par(mfrow = c(1, 2), mar = c(0, 0, 3, 0))
plot_corr_1k <- cor(df_1k %>%
                      dplyr::select(contains("Unit") | contains("Total")))
corrplot(plot_corr_1k, tl.col = 'darkgreen', diag = FALSE, type = "lower",
         order = "hclust", addCoef.col = "darkgreen",
         title = "1k",mar=c(0,0,1,0))

# Plot correlation for df_100k
plot_corr_100k <- cor(df_100k %>%
                        dplyr::select(contains("Unit") | contains("Total")))
corrplot(plot_corr_100k, tl.col = 'darkgreen', diag = FALSE, type = "lower",
         order = "hclust", addCoef.col = "darkgreen",
         title = "100k",mar=c(0,0,1,0))

NA
NA

Skewness is a measure of symmetry, therefore the values near zero, despite one being negative, did not particularly stand out, however for both size data sets, Total - Revenue, Cost and Profit all are right skewed. Skewness = 0: perfect symmetry. Skewness < 0: Negatively is left skewed or has a tail. Skewness > 0: Positive is right skewed or has a right tail.

Concern is not too big with respect to these values as for our model I can try to normalize it as much as possible.

Correlation does more than just support the obvious relationships noted earlier, rather it help identify if we have multicollinearity. Multicollinearity occurs when two or more independent variables in a data frame have a high correlation with one another, and can cause issues with stability and size of an estimated regression coefficient, which in turn makes unreliable inferences for our predictor variables.

Of our variables, Unit.Cost and Total.Profit have the highest correlation, while Unit.-Cost,Price and Sold show the weakest. The way to interpret the correlation is understanding that the higher the absolute value of a correlation coefficient is, the stronger the relationship.

Because I suspect multicollinearity, I’ve chosen to not create my second model off the numeric values, rather I am opting to make a decision tree using one of the categorical values, whose variables I’ve set to factors earlier. For my first I plan to do a simple regression but I suspect normalizing it will not impact the data much.

Model Selection and logic

Simple Linear regression

For my first model I will choose a simple linear regression after normalizing the data.

Normalization

Statology provides a great walk through for normalization. Normalization ensures all variables contribute equally to a model vs having one contribute more because of its value.

# Function for normalization
min_max_norm <- function(x) {
    (x - min(x)) / (max(x) - min(x))
}

# Run function using lapply and only with the numeric values
norm_1k <- as.data.frame(lapply(df_1k %>%
                                    keep(is.numeric) , min_max_norm))

norm_100k <- as.data.frame(lapply(df_100k %>%
                                      keep(is.numeric) , min_max_norm))
#stats
describe(norm_1k, fast=TRUE) %>% 
  dplyr::select(c(-vars,-n))

#distribution
norm_1k %>% 
  gather(variable, value, 1:6) %>%
  ggplot(aes(value)) +
    facet_wrap(~variable, scales = "free") +
    geom_density(fill = "green", alpha=0.9, color="darkgreen") +
    geom_histogram(aes(y=after_stat(density)), alpha=0.2, fill = "pink", 
                   color="darkred", position="identity", bins = 40) +
    theme_minimal()

#stats
describe(norm_100k, fast=TRUE) %>% 
  dplyr::select(c(-vars,-n))

#distribution
norm_100k %>% 
  gather(variable, value, 1:6) %>%
  ggplot(aes(value)) +
    facet_wrap(~variable, scales = "free") +
    geom_density(fill = "green", alpha=0.9, color="darkgreen") +
    geom_histogram(aes(y=after_stat(density)), alpha=0.2, fill = "pink", 
                   color="darkred", position="identity", bins = 40) +
    theme_minimal()

Model

set.seed(777)

simp_reg_sample_1k <- norm_1k$Total.Revenue %>%
  createDataPartition(p = 0.8, list = FALSE)
simp1k_train  <- norm_1k[simp_reg_sample_1k, ]
simp1k_test <- norm_1k[-simp_reg_sample_1k, ]


simp_reg_sample_100k <- norm_100k$Total.Revenue %>%
  createDataPartition(p = 0.8, list = FALSE)
simp100k_train  <- norm_100k[simp_reg_sample_100k, ]
simp100k_test <- norm_100k[-simp_reg_sample_100k, ]
simptrain1k_model<- lm(Total.Revenue~Units.Sold, data=simp1k_train )

summary(simptrain1k_model)

Call:
lm(formula = Total.Revenue ~ Units.Sold, data = simp1k_train)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.38438 -0.11995 -0.02443  0.08641  0.59640 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.007133   0.013649  -0.523    0.601    
Units.Sold   0.411394   0.023203  17.730   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1925 on 798 degrees of freedom
Multiple R-squared:  0.2826,    Adjusted R-squared:  0.2817 
F-statistic: 314.4 on 1 and 798 DF,  p-value: < 2.2e-16
# Make predictions
prediction <- simptrain1k_model %>% predict(simp1k_test)

class(simp1k_test$Total.Revenue)
[1] "numeric"
# Model performance
data.frame(
  MAE = mae(prediction, simp1k_test$Total.Revenue),
  RMSE = RMSE(prediction, simp1k_test$Total.Revenue),
  R2 = R2(prediction, simp1k_test$Total.Revenue)
)
simptrain100k_model<- lm(Total.Revenue~Units.Sold, data=simp100k_train )

summary(simptrain100k_model)

Call:
lm(formula = Total.Revenue ~ Units.Sold, data = simp100k_train)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.38676 -0.11840 -0.02697  0.08723  0.59917 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.0007499  0.0013246  -0.566    0.571    
Units.Sold   0.4015808  0.0022946 175.009   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1873 on 79999 degrees of freedom
Multiple R-squared:  0.2769,    Adjusted R-squared:  0.2768 
F-statistic: 3.063e+04 on 1 and 79999 DF,  p-value: < 2.2e-16
# Make predictions
prediction <- simptrain100k_model %>% predict(simp100k_test)

class(simp100k_test$Total.Revenue)
[1] "numeric"
# Model performance
data.frame(
  MAE = mae(prediction, simp100k_test$Total.Revenue),
  RMSE = RMSE(prediction, simp100k_test$Total.Revenue),
  R2 = R2(prediction, simp100k_test$Total.Revenue)
)

The steps taken for a simple regression were splitting the normalized data into a train and test only using the numeric values. Using Units.Sold as the predictor variable I run my models. The R-squared value of 0.2826 and 0.2769 shows these are terrible models, but that was expected from the EDA. The models accuracy is about 27%-28% which just shows it was not a good model.

Decision Tree

To simplify decision tree, the approach I will use a attribute with a lower number of unique values, but I’ve chosen not to go with Sales.Channel, since this model is very much random and I hope to implement a decision tree with more than 2 possible outcomes for analysis. With this in mind I will make a decision tree model using Region, which I already suspect will create an outcome where Europe and Sub-Saharan Africa are the most likely the regions that will be highlighted in my decision tree, because of its high frequency in the data frames. I will use rpart for my decision tree. NOTE: this will be my first time using rpart, so I am curious on the results.

#split into test/train set

#For df_1k
set.seed(2341)
sample_set <- sample(nrow(df_1k), round(nrow(df_1k)*0.75), replace = FALSE)
df_1k_train <- df_1k[sample_set, ]
df_1k_test <- df_1k[-sample_set, ]

# For df_100k

sample_set <- sample(nrow(df_100k), round(nrow(df_100k)*0.75), replace = FALSE)
df_100k_train <- df_100k[sample_set, ]
df_100k_test <- df_100k[-sample_set, ]

#check class distribution of original, train, and test sets
table_1k<-round(prop.table(table(dplyr::select(df_1k, Region), exclude = NULL)),
            4) * 100
table_1k_train<-round(prop.table(table(dplyr::select(df_1k_train , Region), exclude = NULL)),
            4) * 100
table_1k_test<-round(prop.table(table(dplyr::select(df_1k_test, Region), exclude = NULL)),
            4) * 100

table_100k<-round(prop.table(table(dplyr::select(df_100k, Region), exclude = NULL)),
            4) * 100
table_100k_train<-round(prop.table(table(dplyr::select(df_100k_train, Region), exclude = NULL)),
            4) * 100
table_100k_test<-round(prop.table(table(dplyr::select(df_100k_test, Region), exclude = NULL)),
            4) * 100


as.data.frame(table_1k)
as.data.frame(table_1k_train)
as.data.frame(table_1k_test)

as.data.frame(table_100k)
as.data.frame(table_100k_train)
as.data.frame(table_100k_test)
NA
NA

Incorporating Order.ID in my model kept causing my file to crash despite it not being made into a factor, therefore I opted to remove it, so that I may see the results.

df_1k_train<-df_1k_train%>%
                dplyr::select(-c(Order.ID))
#build model via rpart package
model_1k <- rpart(Region ~ Units.Sold,
                         method = "class",
                         data = df_1k_train,
                         control=rpart.control(minsplit=1, minbucket=1, cp=0.001)
                          )

#display decision tree
# rpart.plot(model_100k)
rpart.plot(model_1k, box.palette = "Blues")

Because the data is undecipherable in this form I opted to make a simpler one with just categorical values.

#build model via rpart package
model_1k <- rpart(Item.Type ~ Order.Priority,
                         method = "class",
                         data = df_1k_train,
                         control=rpart.control(minsplit=1, minbucket=1, cp=0.001)
                          )

#display decision tree
# rpart.plot(model_100k)
rpart.plot(model_1k, box.palette = "Blues")


#build model via rpart package
model_100k <- rpart(Region ~ .-Country,
                         method = "class",
                         data = df_100k_train,
                         control=rpart.control(minsplit=1, minbucket=1, cp=0.001)
                          )

#display decision tree
# rpart.plot(model_100k)
rpart.plot(model_100k, box.palette = "Blues")

Response to Questions

1. Are the columns of your data correlated?

Yes they were. Its apparent in just the relationships involved, such as Country being categorized in Region, and the numeric attributes with label “Total” being derived from their calculations. I also immediately noted the multicollinearity which made it VERY difficult on how I wanted to proceed.

2. Are there labels in your data? Did that impact your choice of algorithm?

No, after checking both data sets, neither had any labels.

3. What are the pros and cons of each algorithm you selected?

The Simple Regression model helped identify the garbage in garbage out data results we wer getting, and because of my familiarity with it I was able to assess and understand the results very easily.

In contrast, this is the first time I’m using a Regression Tree and I am not 100% comfortable with selecting data that is best used for this model. For instance, originally I had decided to select Region and Units.Sold for my tree, but R did not make a useful of even viewable visual. I ended up using to small categories in the 1k data so the result was printable, but in contest with the data, all I can decipher is based on the frequency this is the likelihood of a level of priority based on Item.Type, which is still a somewhat confusing assessment for me. I also read through the cran r_project.org documentation for rpart their is limitations to the amount of factors you may use, forcing me to disregard Country altogether. Using the larger data set I feel a great deal of data was ommitted considersing only 2 regions were represented here.

  1. How your choice of algorithm relates to the datasets (was your choice of algorithm impacted by the datasets you chose)?

I chose simple regression when I figured the data had multicollinearity and assumed that my transformations would not do much to make the data a better fit.

  1. Which result will you trust if you need to make a business decision?

Simple regression. I would have to circle back to business and explain why the data would not be a suitable fit for prediction or analysis.

  1. Do you think an analysis could be prone to errors when using too much data, or when using the least amount possible?

Definitely the Decision Tree, but to be frank operator errors and unfamiliarity with this method is definitely a major factor to account for.

  1. How does the analysis between data sets compare?

No. After I assess the lack of usefulness of the numeric values I opted to make this a learning opportunity in using a decision tree and familiarizing myself with it for future use.

rm(list = ls(pattern = "_tmp$"))
LS0tCnRpdGxlOiAnREFUQSA2MjI6IE1hY2hpbmUgTGVhcm5pbmcgYW5kIEJpZyBEYXRhIEhXMScKYXV0aG9yOiAiR2FicmllbCBDYW1wb3MiCmRhdGU6ICJMYXN0IGVkaXRlZCBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCICVkLCAlWScpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBnZW9tZXRyeTogbGVmdD0wLjVjbSxyaWdodD0wLjVjbSx0b3A9MWNtLGJvdHRvbT0yY20KICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgcGRmX2RvY3VtZW50OgogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4CnVybGNvbG9yOiBibHVlCi0tLQoKCgojIFBhY2thZ2VzCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkocmVhZHIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRpZHltb2RlbHMpCmxpYnJhcnkocHN5Y2gpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkocnBhcnQpCmxpYnJhcnkocnBhcnQucGxvdCkKbGlicmFyeShjb3JycGxvdCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkobGFiZWxsZWQpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ2ZvcmNlKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KE1ldHJpY3MpCmBgYAoKIyBJbnN0cnVjdGlvbnMKCiMjICoqRXhwbG9yYXRvcnkgYW5hbHlzaXMgYW5kIGVzc2F5KioKCiMjICpQcmUtd29yayoKCjEuIFZpc2l0IHRoZSBmb2xsb3dpbmcgd2Vic2l0ZSBhbmQgZXhwbG9yZSB0aGUgcmFuZ2Ugb2Ygc2l6ZXMgb2YgdGhpcyBkYXRhc2V0IChmcm9tIDEwMCB0byA1IG1pbGxpb24gcmVjb3Jkcyk6Cmh0dHBzOi8vZXhjZWxiaWFuYWx5dGljcy5jb20vd3AvZG93bmxvYWRzLTE4LXNhbXBsZS1jc3YtZmlsZXMtZGF0YS1zZXRzLWZvci10ZXN0aW5nLXNhbGVzLyBvciAobmV3KSBodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzCjIuIFNlbGVjdCAyIGZpbGVzIHRvIGRvd25sb2FkCiAgICBCYXNlZCBvbiB5b3VyIGNvbXB1dGVyJ3MgY2FwYWJpbGl0aWVzIChtZW1vcnksIENQVSksIHNlbGVjdCAyIGZpbGVzIHlvdSBjYW4gaGFuZGxlIChyZWNvbW1lbmRlZCBvbmUgc21hbGwsIG9uZSBsYXJnZSkKMy4gRG93bmxvYWQgdGhlIGZpbGVzCjQuIFJldmlldyB0aGUgc3RydWN0dXJlIGFuZCBjb250ZW50IG9mIHRoZSB0YWJsZXMsIGFuZCB0aGluayBhYm91dCB0aGUgZGF0YSBzZXRzIChzdHJ1Y3R1cmUsIHNpemUsIGRlcGVuZGVuY2llcywgbGFiZWxzLCBldGMpCjUuIENvbnNpZGVyIHRoZSBzaW1pbGFyaXRpZXMgYW5kIGRpZmZlcmVuY2VzIGluIHRoZSB0d28gZGF0YSBzZXRzIHlvdSBoYXZlIGRvd25sb2FkZWQKNi4gVGhpbmsgYWJvdXQgaG93IHRvIGFuYWx5emUgYW5kIHByZWRpY3QgYW4gb3V0Y29tZSBiYXNlZCBvbiB0aGUgZGF0YXNldHMgYXZhaWxhYmxlCjcuIEJhc2VkIG9uIHRoZSBkYXRhIHlvdSBoYXZlLCB0aGluayB3aGljaCB0d28gbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG1zIHByZXNlbnRlZCBzbyBmYXIgY291bGQgYmUgdXNlZCB0byBhbmFseXplIHRoZSBkYXRhCgojIyAqRGVsaXZlcmFibGUqCgoxLiBFc3NheSAobWluaW11bSA1MDAgd29yZCBkb2N1bWVudCkKICBXcml0ZSBhIHNob3J0IGVzc2F5IGV4cGxhaW5pbmcgeW91ciBzZWxlY3Rpb24gb2YgYWxnb3JpdGhtcyBhbmQgaG93IHRoZXkgcmVsYXRlIHRvIHRoZSBkYXRhIGFuZCB3aGF0IHlvdSBhcmUgdHJ5aW5nIHRvIGRvCjIuIEV4cGxvcmF0b3J5IEFuYWx5c2lzICgqKkVEQSoqKSB1c2luZyBSIG9yIFB5dGhvbiAoc3VibWl0IGNvZGUgKyBlcnJvcnMgKyBhbmFseXNpcyBhcyBub3RlYm9vayBvciBjb3B5L3Bhc3RlIHRvIGRvY3VtZW50KQogIEV4cGxvcmUgaG93IHRvIGFuYWx5emUgYW5kIHByZWRpY3QgYW4gb3V0Y29tZSBiYXNlZCBvbiB0aGUgZGF0YSBhdmFpbGFibGUuIFRoaXMgd2lsbCBiZSBhbiBleHBsb3JhdG9yeSBleGVyY2lzZSwgc28gZmVlbCBmcmVlIHRvIHNob3cgZXJyb3JzIGFuZCB3YXJuaW5ncyB0aGF0IHJhaXNlIGR1cmluZyB0aGUgYW5hbHlzaXMuIFRlc3QgdGhlIGNvZGUgd2l0aCBib3RoIGRhdGFzZXRzIHNlbGVjdGVkIGFuZCBjb21wYXJlIHRoZSByZXN1bHRzLgoKIyMgKkFuc3dlciBxdWVzdGlvbnMgc3VjaCBhczoqCjEuIEFyZSB0aGUgY29sdW1ucyBvZiB5b3VyIGRhdGEgY29ycmVsYXRlZD8KMi4gQXJlIHRoZXJlIGxhYmVscyBpbiB5b3VyIGRhdGE/IERpZCB0aGF0IGltcGFjdCB5b3VyIGNob2ljZSBvZiBhbGdvcml0aG0/CjMuIFdoYXQgYXJlIHRoZSBwcm9zIGFuZCBjb25zIG9mIGVhY2ggYWxnb3JpdGhtIHlvdSBzZWxlY3RlZD8KNC4gSG93IHlvdXIgY2hvaWNlIG9mIGFsZ29yaXRobSByZWxhdGVzIHRvIHRoZSBkYXRhc2V0cyAod2FzIHlvdXIgY2hvaWNlIG9mIGFsZ29yaXRobSBpbXBhY3RlZCBieSB0aGUgZGF0YXNldHMgeW91IGNob3NlKT8KNS4gV2hpY2ggcmVzdWx0IHdpbGwgeW91IHRydXN0IGlmIHlvdSBuZWVkIHRvIG1ha2UgYSBidXNpbmVzcyBkZWNpc2lvbj8KNi4gRG8geW91IHRoaW5rIGFuIGFuYWx5c2lzIGNvdWxkIGJlIHByb25lIHRvIGVycm9ycyB3aGVuIHVzaW5nIHRvbyBtdWNoIGRhdGEsIG9yIHdoZW4gdXNpbmcgdGhlIGxlYXN0IGFtb3VudCBwb3NzaWJsZT8KNy4gSG93IGRvZXMgdGhlIGFuYWx5c2lzIGJldHdlZW4gZGF0YSBzZXRzIGNvbXBhcmU/CgpEZXZlbG9wIHlvdXIgZXhwbG9yYXRvcnkgYW5hbHlzaXMgb2YgdGhlIGRhdGEgYW5kIHRoZSBlc3NheSBpbiB0aGUgZm9sbG93aW5nIDIgd2Vla3MuCgojIERBVEEKCmBgYHtyfQp1cmw8LSJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vR2l0YWJsZUdhYmUvRGF0YTYyNF9EYXRhL21haW4vIgpkZl8xayA8LSByZWFkLmNzdihwYXN0ZTAodXJsLCIxMDAwJTIwU2FsZXMlMjBSZWNvcmRzLmNzdiIpKQpkZl8xMDBrIDwtIHJlYWQuY3N2KHBhc3RlMCh1cmwsIjEwMDAwMCUyMFNhbGVzJTIwUmVjb3Jkcy5jc3YiKSkKYGBgCgojICoqRURBKioKCkZhbWlsaWFyaXphdGlvbiB3aXRoIFNhbGVzIGRhdGFzZXRzIGV4dHJhY3RlZCBmcm9tIFtleGNlbGJpIGFuYWx5dGljc10oaHR0cHM6Ly9leGNlbGJpYW5hbHl0aWNzLmNvbS93cC9kb3dubG9hZHMtMTgtc2FtcGxlLWNzdi1maWxlcy1kYXRhLXNldHMtZm9yLXRlc3Rpbmctc2FsZXMvKSByZXF1aXJlcyB1bmRlcnN0YW5kaW5nIG9mIGRhdGFzZXQgY29tcG9zaXRpb24sIGRpbWVuc2lvbnMsIGNvbHVtbiB0eXBlcywgYE5BYCBvciBgTnVsbGAgdmFsdWUgY291bnQsIGV0Yy4gCgojIyBEYXRhIENvbXBvc2l0aW9uCgpgYGB7cn0Kc3RyKGRmXzFrKQpgYGAKCmBgYHtyfQpzdHIoZGZfMTAwaykKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0yfQprYWJsZShhcy5kYXRhLmZyYW1lKHRhYmxlKGRmXzFrJFJlZ2lvbikpICU+JSBhcnJhbmdlKGRlc2MoRnJlcSkpLAogICAgICBjYXB0aW9uID0gIkZyZXF1ZW5jeSBSZWdpb24gZGZfMWsiKQpgYGAKCmBgYHtyfQprYWJsZShhcy5kYXRhLmZyYW1lKHRhYmxlKGRmXzEwMGskUmVnaW9uKSkgJT4lIGFycmFuZ2UoZGVzYyhGcmVxKSksCiAgICAgIGNhcHRpb24gPSAiRnJlcXVlbmN5IFJlZ2lvbiBkZl8xMDBrIikKYGBgCgoKYGBge3J9CmthYmxlKGFzLmRhdGEuZnJhbWUodGFibGUoZGZfMWskSXRlbS5UeXBlICkpICU+JSBhcnJhbmdlKGRlc2MoRnJlcSkpLAogICAgICBjYXB0aW9uID0gIkZyZXF1ZW5jeSBJdGVtLlR5cGUgZGZfMWsiKQpgYGAKCgpgYGB7cn0Ka2FibGUoYXMuZGF0YS5mcmFtZSh0YWJsZShkZl8xMDBrJEl0ZW0uVHlwZSApKSAlPiUgYXJyYW5nZShkZXNjKEZyZXEpKSwKICAgICAgICAgICAgY2FwdGlvbiA9ICJGcmVxdWVuY3kgSXRlbSBUeXBlIDEwMGsiKQpgYGAKCgpgYGB7cn0Ka2FibGUoYXMuZGF0YS5mcmFtZSh0YWJsZShkZl8xayRTYWxlcy5DaGFubmVsICkpICU+JSBhcnJhbmdlKGRlc2MoRnJlcSkpLAogICAgICBjYXB0aW9uID0gIkZyZXF1ZW5jeSBTYWxlcyBDaGFubmVsIDFrIikKYGBgCgpgYGB7cn0Ka2FibGUoYXMuZGF0YS5mcmFtZSh0YWJsZShkZl8xMDBrJFNhbGVzLkNoYW5uZWwgKSkgJT4lIGFycmFuZ2UoZGVzYyhGcmVxKSksCiAgICAgIGNhcHRpb24gPSAiRnJlcXVlbmN5IFNhbGVzIENoYW5uZWwgMTAwayIpCmBgYAoKYGBge3J9CnZhcl9sYWJlbChkZl8xaykKYGBgCgpgYGB7cn0KdmFyX2xhYmVsKGRmXzEwMGspCmBgYAoKCmBgYHtyfQojIERpbWVuc2lvbnMKZGltXzFrX3RtcDwtZGltKGRmXzFrKQpkaW1fMTAwa190bXA8LWRpbShkZl8xMDBrKQojIENsYXNzCmNsYXNzXzFrX3RtcDwtc2FwcGx5KGRmXzFrLGNsYXNzKQpjbGFzc18xMDBrX3RtcDwtc2FwcGx5KGRmXzEwMGssY2xhc3MpCgoKCmNvbHVtbl9uYW1lXzFrX3RtcCA8LSAiT3JkZXIuSUQiCgojIENvdW50IHRoZSBudW1iZXIgb2YgZHVwbGljYXRlcyBpbiB0aGUgc3BlY2lmaWVkIGNvbHVtbgpudW1fZHVwbGljYXRlc18xa190bXAgPC0gc3VtKGR1cGxpY2F0ZWQoZGZfMWtbW2NvbHVtbl9uYW1lXzFrX3RtcF1dKSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkdXBsaWNhdGVkKGRmXzFrW1tjb2x1bW5fbmFtZV8xa190bXBdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJvbUxhc3QgPSBUUlVFKSkKCmNvbHVtbl9uYW1lXzEwMGtfdG1wIDwtICJPcmRlci5JRCIKCiMgQ291bnQgdGhlIG51bWJlciBvZiBkdXBsaWNhdGVzIGluIHRoZSBzcGVjaWZpZWQgY29sdW1uCm51bV9kdXBsaWNhdGVzXzEwMGtfdG1wIDwtIHN1bShkdXBsaWNhdGVkKGRmXzEwMGtbW2NvbHVtbl9uYW1lXzEwMGtfdG1wXV0pIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGR1cGxpY2F0ZWQoZGZfMTAwa1tbY29sdW1uX25hbWVfMTAwa190bXBdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJvbUxhc3QgPSBUUlVFKSkKCgpuYV9udWxsX2NudF90bXA8LShzdW0oY29sU3Vtcyhpcy5uYShkZl8xaykgfCBpcy5udWxsKGRmXzFrKSkpKwogICAgICAgICAgICAgICAgICAgIHN1bShjb2xTdW1zKGlzLm5hKGRmXzEwMGspIHwgaXMubnVsbChkZl8xMDBrKSkpKQoKcmVnaW9uX3RtcDwtdW5pcXVlKGRmXzFrJFJlZ2lvbikKY291bnRyeV9sZW5fdG1wPC1sZW5ndGgodW5pcXVlKGRmXzFrJENvdW50cnkpKQoKYGBgCgoKVGhlIGRhdGFzZXQgb2Ygc2l6ZSAxMDAwIGlzIHN0b3JlZCB0byBgZGZfMWtgIGFuZCB0aGUgZGF0YXNldCBzaXplIDEwMCwwMDAgaXMgc3RvcmVkIHRvIGBkZl8xMDBrYAoKKiBgZGZfMWtgIGRpbWVuc2lvbnMgaXMgYHIgZGltXzFrX3RtcFsxXWAgcm93cyBhbmQgYHIgZGltXzFrX3RtcFsyXWAgY29sdW1ucy4gCiogYGRmXzEwMGtgIGRpbWVuc2lvbnMgaXMgYHIgZGltXzEwMGtfdG1wWzFdYCByb3dzIGFuZCBgciBkaW1fMTAwa190bXBbMl1gIGNvbHVtbnMuCiogVGhlIGNvbHVtbiB0eXBlcyBmb3IgZGZfMWsgYXJlCmByIGNsYXNzXzFrX3RtcGAgCiogVGhlIGNvbHVtbiB0eXBlcyBmb3IgZGZfMTAwayBhcmUgCmByIGNsYXNzXzEwMGtfdG1wYAoqIE5vdGFibGUgY2F0ZWdvcmllcyBpbmNsdWRlCiAgLSBgT3JkZXIuRGF0ZWAgYW5kIGBTaGlwLkRhdGVgIHRoZSBvbmx5IGRhdGUgdmFsdWVkIGNvbHVtbnMsIGJ1dCBzZXQgdG8gdHlwZSBgY2hyYCBhbmQgbWF5IG5lZWQgY29udmVydGluZy4KICAtIGBPcmRlci5JRGAgaXMgY29tcG9zZSBvZiB1bmlxdWUgdmFsdWVzIHdpdGggYHIgbnVtX2R1cGxpY2F0ZXNfMWtfdG1wYCBkdXBsaWNhdGVzIGZvdW5kIGluIHRoZSBgZGZfMWtgIGRhdGEgYW5kIGByIG51bV9kdXBsaWNhdGVzXzEwMGtfdG1wYCBmb3VuZCBpbiB0aGUgYGRmXzEwMGtgIGRhdGEuCiAgLSBgUmVnaW9uYCBhbmQgYENvdW50cnlgIGJvdGggb2Ygd2hpY2ggZGVmaW5lIGxvY2F0aW9uCiAgLSBgSXRlbS5UeXBlYCBmb3IgdHlwZSBvZiBpdGVtIHNvbGQuCiAgLSBgU2FsZXMuQ2hhbm5lbGAgZGVmaW5lcyBzYWxlcyBtZXRob2QgYXMgYW4gb25saW5lIG9yIG9mZmxpbmUgcHVyY2hhc2UsIG9yIGUtcHVyY2hhc2UgdnMgaW4tc3RvcmUuCiAgLSBgT3JkZXIuUHJpb3JpdHlgIHdoaWNoIGhhcyBhIHJhbmtpbmcgb2Ygc2V2ZXJpdHkuCiAgLSBBdHRyaWJ1dGVzIGxhYmVsZWQgd2l0aCBgVG90YWxgIHRoYXQgYXJlIGNhbGN1bGF0ZWQgdmFsdWVzLgogICogVXNpbmcgdGhlIGBsZW5ndGgoKWAgZnVuY3Rpb25zIHdlIHNlZSB0aGF0IGByIGNvdW50cnlfbGVuX3RtcGAgY291bnRyaWVzIGFyZSBsaXN0ZWQgaW4gdGhlIGRhdGEuCiogVXNpbmcgdGhlIGB0YWJsZWAgZnVuY3Rpb24gd2Ugc2VlOgogICAgLU9mIHRoZSBgUmVnaW9uc2AgbGlzdGVkIGBTdWItU2FoYXJhbiBBZnJpY2FgIGFuZCBgRXVyb3BlYCBpcyBtb3N0IGZyZXF1ZW50ZWQuCiAgICAtIEZvciBgZGZfMWtgIGBCZXZlcmFnZXNgIGFuZCBgVmVnZXRhYmxlc2AgaXMgbW9zdCBmcmVxdWVudGVkLCBob3dldmVyIHdpdGggYGRmXzEwMGtgIGBPZmZpY2UgU3VwcGxpZXNgIGFuZCBgQ2VyZWFsc2AgaXMuCiAgICAtIEZvciBgZGZfMWtgIG1vcmUgcHVyY2hhc2VzIGFyZSBkb25lIGBPZmZsaW5lYCB3aGlsZSBmb3IgYGRmXzEwMGtgIG1vcmUgaXMgZG9uZSBgT25saW5lYCBBbGJpZXQgYnkgYSBzbWFsbCBtYXJnaW4gaW4gYm90aCBjYXNlcy4KCldpdGggcmVzcGVjdCB0byBkZXBlbmRlbmNpZXMsIHRoZSBmb3JtdWxhcyBiZWxvdyBoaWdobGlnaHQgdGhlIGRlcGVuZGVuY3kgdGhhdCBleGlzdHMgd2l0aCBjYWxjdWxhdGVkIHZhcmlhYmxlcyB3aXRoIHRoZSBsYWJlbCBgVG90YWxgIGluIHRoZXJlIEF0dHJpYnV0ZSBuYW1lLgoKJFRvdGFsLkNvc3Q9VW5pdHMuU29sZFx0aW1lcyBVbml0LkNvc3QkIG1ha2luZyBUb3RhbC5Db3N0IGRlcGVuZGVudCBvbiBVbml0cy5Tb2xkIGFuZCBVbml0IENvc3QKJFRvdGFsLlJldmVudWU9VW5pdHMuU29sZFx0aW1lcyBVbml0LlByaWNlJCBtYWtpbmcgVG90YWwuUmV2ZW51ZSBkZXBlbmRlbnQgb24gVW5pdHMuU29sZCBhbmQgVW5pdC5QcmljZQokVG90YWwuUHJvZml0PVRvdGFsLlJldmVudWUtVG90YWwuQ29zdCQgbWFraW5nIHRoZSBzdWJzZXF1ZW50IHRvdGFscyBhYm92ZSB0aGUgZGVwZW5kZW50IHZhcmlhYmxlcyBmb3IgVG90YWwuUHJvZml0CgpUaGUgT3JkZXIuUHJpb3JpdHkgaGF2ZSBhIGRlcGVuZGVuY3kgYmFzZWQgb24gcmFua2luZyBvZiBgciB1bmlxdWUoZGZfMWskT3JkZXIuUHJpb3JpdHkpYApXaGljaCBpcyBDcml0aWNhbCwgSGlnaCwgTWVkaXVtLCBMb3cgaW4gYXNjZW5kaW5nIG9yZGVyLgoKRGF0ZSB2YWx1ZXMgYXJlIGRlcGVuZGVudCBpbiBpbnRlcnByZXRhdGlvbiwgd2l0aCBjYWxjdWxhdGlvbiBvZiBgT3JkZXIuRGF0ZWAgYW5kIGBTaGlwLkRhdGVgIGJlaW5nIGEgZmFjdG9yIG9mIHBlcmZvcm1hbmNlIG9yIHRpbWVsaW5lc3MuCgoKIyMgRGF0YSBUcmFuZm9ybWF0aW9uCgpgYGB7cn0KZGZfMWtbWydPcmRlci5EYXRlJ11dIDwtIGFzLkRhdGUoZGZfMWtbWydPcmRlci5EYXRlJ11dLCAiJW0vJWQvJVkiKQpkZl8xa1tbJ1NoaXAuRGF0ZSddXSA8LSBhcy5EYXRlKGRmXzFrW1snU2hpcC5EYXRlJ11dLCAiJW0vJWQvJVkiKQoKZGZfMTAwa1tbJ09yZGVyLkRhdGUnXV0gPC0gYXMuRGF0ZShkZl8xMDBrW1snT3JkZXIuRGF0ZSddXSwgIiVtLyVkLyVZIikKZGZfMTAwa1tbJ1NoaXAuRGF0ZSddXSA8LSBhcy5EYXRlKGRmXzEwMGtbWydTaGlwLkRhdGUnXV0sICIlbS8lZC8lWSIpCgpkZl8xayRPcmRlci5Qcmlvcml0eSA8LSBhcy5mYWN0b3IoZGZfMWskT3JkZXIuUHJpb3JpdHkpCmRmXzEwMGskT3JkZXIuUHJpb3JpdHkgPC0gYXMuZmFjdG9yKGRmXzEwMGskT3JkZXIuUHJpb3JpdHkpCmBgYAoKVGhlIG1vc3Qgb2J2aW91cyB0cmFuc2Zvcm1hdGlvbnMgd2VyZSB0aGUgZGF0ZSB2YWx1ZXMgYXMgbm90ZWQgaW4gKkVEQSogYW5kIGZhY3RvcmluZyB0aGUgY2F0ZWdvcmllcyBpbiBgT3JkZXIuUHJpb3JpdHlgCgpgYGB7cn0KZGZfMWskU2FsZXMuQ2hhbm5lbCA8LSBhcy5mYWN0b3IoZGZfMWskU2FsZXMuQ2hhbm5lbCkKZGZfMTAwayRTYWxlcy5DaGFubmVsIDwtIGFzLmZhY3RvcihkZl8xMDBrJFNhbGVzLkNoYW5uZWwpCmRmXzFrJEl0ZW0uVHlwZSA8LSBhcy5mYWN0b3IoZGZfMWskSXRlbS5UeXBlKQpkZl8xMDBrJEl0ZW0uVHlwZSA8LSBhcy5mYWN0b3IoZGZfMTAwayRJdGVtLlR5cGUpCmRmXzFrJFJlZ2lvbiA8LSBhcy5mYWN0b3IoZGZfMWskUmVnaW9uKQpkZl8xMDBrJFJlZ2lvbiA8LSBhcy5mYWN0b3IoZGZfMTAwayRSZWdpb24pCmRmXzFrJENvdW50cnkgPC0gYXMuZmFjdG9yKGRmXzFrJENvdW50cnkpCmRmXzEwMGskQ291bnRyeSA8LSBhcy5mYWN0b3IoZGZfMTAwayRDb3VudHJ5KQoKYGBgCgpgU2FsZXMuQ2hhbm5lbGAsYEl0ZW0uVHlwZWAgYW5kIGBSZWdpb25gIHdlcmUgYWxzbyBsb2dpY2FsIGNob2ljZXMsIGNvbnNpZGVyaW5nIHRoZSBhbW91bnQgb2YgdW5pcXVlIHZhbHVlcyBmb3IgYENvdW50cnlgIGFuZCB0aGUgbmF0dXJlIG9mIGl0cyByZWxhdGlvbnNoaXAgd2l0aCBSZWdpb24sIEkgYmVsaWV2ZSBpZiBJIG1ha2UgYSBtb2RlbCB3aXRoIFJlZ2lvbiwgQ291bnRyeSB3b3VsZCBiZSBleGNsdWRlZC4KT3JkZXIuSUQgYXJlIGp1c3QgYXJiaXRyYXJ5LCBjaHJvbm9sb2dpY2FsIG9yIGluY3JlbWVudGVkIG51bWJlcnMgdGhlcmVmb3IgaXQgd2FzIG5vdCBzZXQgYXMgYSBmYWN0b3IuCgpgYGB7cn0KbGV2ZWxzKGRmXzFrJFJlZ2lvbikKYGBgCgoKIyMgQ29ycmVsYXRpb24gYW5kIFNrZXduZXNzCgpgYGB7cn0KZGVzY3JpYmUoZGZfMWslPiUKICAgICAgICAgICBkcGx5cjo6c2VsZWN0KGNvbnRhaW5zKCJVbml0IikgfCBjb250YWlucygiVG90YWwiKSkpICU+JSAKICBkcGx5cjo6c2VsZWN0KGMobWVhbixzZCxtaW4sbWF4LHJhbmdlLHNlLHNrZXcpKQpgYGAKCmBgYHtyfQpkZXNjcmliZShkZl8xMDBrJT4lCiAgICAgICAgICAgZHBseXI6OnNlbGVjdChjb250YWlucygiVW5pdCIpIHwgY29udGFpbnMoIlRvdGFsIikpKSAlPiUgCiAgZHBseXI6OnNlbGVjdChjKG1lYW4sc2QsbWluLG1heCxyYW5nZSxzZSxza2V3KSkKYGBgCgpgYGB7cn0KcGxvdF9udW1lcmljXzFrPC1kZl8xayU+JQogICAgICBkcGx5cjo6c2VsZWN0KGNvbnRhaW5zKCJVbml0IikgfCBjb250YWlucygiVG90YWwiKSkgJT4lCiAgZ2F0aGVyKHZhcmlhYmxlLCB2YWx1ZSwgMTo2KSAlPiUKICBnZ3Bsb3QoYWVzKHZhbHVlKSkgKwogICAgZmFjZXRfd3JhcCh+dmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIikgKwogICAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAibGlnaHRncmVlbiIsIGFscGhhPTAuOSwgY29sb3I9ImxpZ2h0Z3JlZW4iKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeT1hZnRlcl9zdGF0KGRlbnNpdHkpKSwgYWxwaGE9MC4yLCBmaWxsID0gImxpZ2h0Ymx1ZSIsCiAgICAgICAgICAgICAgICAgICBjb2xvcj0iZGFya3JlZCIsIHBvc2l0aW9uPSJpZGVudGl0eSIsIGJpbnMgPSA0MCkKCnBsb3RfbnVtZXJpY18xMDBrPC1kZl8xMDBrJT4lCiAgICAgIGRwbHlyOjpzZWxlY3QoY29udGFpbnMoIlVuaXQiKSB8IGNvbnRhaW5zKCJUb3RhbCIpKSAlPiUKICBnYXRoZXIodmFyaWFibGUsIHZhbHVlLCAxOjYpICU+JQogIGdncGxvdChhZXModmFsdWUpKSArCiAgICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArCiAgICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJsaWdodGdyZWVuIiwgYWxwaGE9MC45LCBjb2xvcj0ibGlnaHRncmVlbiIpICsKICAgIGdlb21faGlzdG9ncmFtKGFlcyh5PWFmdGVyX3N0YXQoZGVuc2l0eSkpLCBhbHBoYT0wLjIsIGZpbGwgPSAicGluayIsCiAgICAgICAgICAgICAgICAgICBjb2xvcj0iZGFya3JlZCIsIHBvc2l0aW9uPSJpZGVudGl0eSIsIGJpbnMgPSA0MCkKCmdyaWQuYXJyYW5nZShwbG90X251bWVyaWNfMWsscGxvdF9udW1lcmljXzEwMGssbmNvbD0xKQpgYGAKCmBgYHtyfQpwYXIobWZyb3cgPSBjKDEsIDIpLCBtYXIgPSBjKDAsIDAsIDMsIDApKQpwbG90X2NvcnJfMWsgPC0gY29yKGRmXzFrICU+JQogICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChjb250YWlucygiVW5pdCIpIHwgY29udGFpbnMoIlRvdGFsIikpKQpjb3JycGxvdChwbG90X2NvcnJfMWssIHRsLmNvbCA9ICdkYXJrZ3JlZW4nLCBkaWFnID0gRkFMU0UsIHR5cGUgPSAibG93ZXIiLAogICAgICAgICBvcmRlciA9ICJoY2x1c3QiLCBhZGRDb2VmLmNvbCA9ICJkYXJrZ3JlZW4iLAogICAgICAgICB0aXRsZSA9ICIxayIsbWFyPWMoMCwwLDEsMCkpCgojIFBsb3QgY29ycmVsYXRpb24gZm9yIGRmXzEwMGsKcGxvdF9jb3JyXzEwMGsgPC0gY29yKGRmXzEwMGsgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoY29udGFpbnMoIlVuaXQiKSB8IGNvbnRhaW5zKCJUb3RhbCIpKSkKY29ycnBsb3QocGxvdF9jb3JyXzEwMGssIHRsLmNvbCA9ICdkYXJrZ3JlZW4nLCBkaWFnID0gRkFMU0UsIHR5cGUgPSAibG93ZXIiLAogICAgICAgICBvcmRlciA9ICJoY2x1c3QiLCBhZGRDb2VmLmNvbCA9ICJkYXJrZ3JlZW4iLAogICAgICAgICB0aXRsZSA9ICIxMDBrIixtYXI9YygwLDAsMSwwKSkKCgpgYGAKClNrZXduZXNzIGlzIGEgbWVhc3VyZSBvZiBzeW1tZXRyeSwgdGhlcmVmb3JlIHRoZSB2YWx1ZXMgbmVhciB6ZXJvLCBkZXNwaXRlIG9uZSBiZWluZyBuZWdhdGl2ZSwgZGlkIG5vdCBwYXJ0aWN1bGFybHkgc3RhbmQgb3V0LCBob3dldmVyIGZvciBib3RoIHNpemUgZGF0YSBzZXRzLCBUb3RhbCAtIFJldmVudWUsIENvc3QgYW5kIFByb2ZpdCBhbGwgYXJlIHJpZ2h0IHNrZXdlZC4KICAgIFNrZXduZXNzID0gMDogcGVyZmVjdCBzeW1tZXRyeS4KICAgIFNrZXduZXNzIDwgMDogTmVnYXRpdmVseSBpcyBsZWZ0IHNrZXdlZCBvciBoYXMgYSB0YWlsLgogICAgU2tld25lc3MgPiAwOiBQb3NpdGl2ZSBpcyByaWdodCBza2V3ZWQgb3IgaGFzIGEgcmlnaHQgdGFpbC4KICAgIApDb25jZXJuIGlzIG5vdCB0b28gYmlnIHdpdGggcmVzcGVjdCB0byB0aGVzZSB2YWx1ZXMgYXMgZm9yIG91ciBtb2RlbCBJIGNhbiB0cnkgdG8gbm9ybWFsaXplIGl0IGFzIG11Y2ggYXMgcG9zc2libGUuIAoKQ29ycmVsYXRpb24gZG9lcyBtb3JlIHRoYW4ganVzdCBzdXBwb3J0IHRoZSBvYnZpb3VzIHJlbGF0aW9uc2hpcHMgbm90ZWQgZWFybGllciwgcmF0aGVyIGl0IGhlbHAgaWRlbnRpZnkgaWYgd2UgaGF2ZSBtdWx0aWNvbGxpbmVhcml0eS4gTXVsdGljb2xsaW5lYXJpdHkgb2NjdXJzIHdoZW4gdHdvIG9yIG1vcmUgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGluIGEgZGF0YSBmcmFtZSBoYXZlIGEgaGlnaCBjb3JyZWxhdGlvbiB3aXRoIG9uZSBhbm90aGVyLCBhbmQgY2FuIGNhdXNlIGlzc3VlcyB3aXRoIHN0YWJpbGl0eSBhbmQgc2l6ZSBvZiBhbiBlc3RpbWF0ZWQgcmVncmVzc2lvbiBjb2VmZmljaWVudCwgd2hpY2ggaW4gdHVybiBtYWtlcyB1bnJlbGlhYmxlIGluZmVyZW5jZXMgZm9yIG91ciBwcmVkaWN0b3IgdmFyaWFibGVzLgoKT2Ygb3VyIHZhcmlhYmxlcywgVW5pdC5Db3N0IGFuZCBUb3RhbC5Qcm9maXQgaGF2ZSB0aGUgaGlnaGVzdCBjb3JyZWxhdGlvbiwgd2hpbGUgVW5pdC4tQ29zdCxQcmljZSBhbmQgU29sZCBzaG93IHRoZSB3ZWFrZXN0LiBUaGUgd2F5IHRvIGludGVycHJldCB0aGUgY29ycmVsYXRpb24gaXMgdW5kZXJzdGFuZGluZyB0aGF0IHRoZSBoaWdoZXIgdGhlIGFic29sdXRlIHZhbHVlIG9mIGEgY29ycmVsYXRpb24gY29lZmZpY2llbnQgaXMsIHRoZSBzdHJvbmdlciB0aGUgcmVsYXRpb25zaGlwLgoKQmVjYXVzZSBJIHN1c3BlY3QgbXVsdGljb2xsaW5lYXJpdHksIEkndmUgY2hvc2VuIHRvIG5vdCBjcmVhdGUgbXkgc2Vjb25kIG1vZGVsIG9mZiB0aGUgbnVtZXJpYyB2YWx1ZXMsIHJhdGhlciBJIGFtIG9wdGluZyB0byBtYWtlIGEgZGVjaXNpb24gdHJlZSB1c2luZyBvbmUgb2YgdGhlIGNhdGVnb3JpY2FsIHZhbHVlcywgd2hvc2UgdmFyaWFibGVzIEkndmUgc2V0IHRvIGZhY3RvcnMgZWFybGllci4gRm9yIG15IGZpcnN0IEkgcGxhbiB0byBkbyBhIHNpbXBsZSByZWdyZXNzaW9uIGJ1dCBJIHN1c3BlY3Qgbm9ybWFsaXppbmcgaXQgd2lsbCBub3QgaW1wYWN0IHRoZSBkYXRhIG11Y2guCgojIE1vZGVsIFNlbGVjdGlvbiBhbmQgbG9naWMKCgojIyBTaW1wbGUgTGluZWFyIHJlZ3Jlc3Npb24KRm9yIG15IGZpcnN0IG1vZGVsIEkgd2lsbCBjaG9vc2UgYSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gYWZ0ZXIgbm9ybWFsaXppbmcgdGhlIGRhdGEuCgojIyMgTm9ybWFsaXphdGlvbgoKW1N0YXRvbG9neV0oaHR0cHM6Ly93d3cuc3RhdG9sb2d5Lm9yZy9ob3ctdG8tbm9ybWFsaXplLWRhdGEtaW4tci8pIHByb3ZpZGVzIGEgZ3JlYXQgd2FsayB0aHJvdWdoIGZvciBub3JtYWxpemF0aW9uLgpOb3JtYWxpemF0aW9uIGVuc3VyZXMgYWxsIHZhcmlhYmxlcyBjb250cmlidXRlIGVxdWFsbHkgdG8gYSBtb2RlbCB2cyBoYXZpbmcgb25lIGNvbnRyaWJ1dGUgbW9yZSBiZWNhdXNlIG9mIGl0cyB2YWx1ZS4KCgpgYGB7cn0KIyBGdW5jdGlvbiBmb3Igbm9ybWFsaXphdGlvbgptaW5fbWF4X25vcm0gPC0gZnVuY3Rpb24oeCkgewogICAgKHggLSBtaW4oeCkpIC8gKG1heCh4KSAtIG1pbih4KSkKfQoKIyBSdW4gZnVuY3Rpb24gdXNpbmcgbGFwcGx5IGFuZCBvbmx5IHdpdGggdGhlIG51bWVyaWMgdmFsdWVzCm5vcm1fMWsgPC0gYXMuZGF0YS5mcmFtZShsYXBwbHkoZGZfMWsgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXAoaXMubnVtZXJpYykgLCBtaW5fbWF4X25vcm0pKQoKbm9ybV8xMDBrIDwtIGFzLmRhdGEuZnJhbWUobGFwcGx5KGRmXzEwMGsgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2VlcChpcy5udW1lcmljKSAsIG1pbl9tYXhfbm9ybSkpCmBgYAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KI3N0YXRzCmRlc2NyaWJlKG5vcm1fMWssIGZhc3Q9VFJVRSkgJT4lIAogIGRwbHlyOjpzZWxlY3QoYygtdmFycywtbikpCgojZGlzdHJpYnV0aW9uCm5vcm1fMWsgJT4lIAogIGdhdGhlcih2YXJpYWJsZSwgdmFsdWUsIDE6NikgJT4lCiAgZ2dwbG90KGFlcyh2YWx1ZSkpICsKICAgIGZhY2V0X3dyYXAofnZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZSIpICsKICAgIGdlb21fZGVuc2l0eShmaWxsID0gImdyZWVuIiwgYWxwaGE9MC45LCBjb2xvcj0iZGFya2dyZWVuIikgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9YWZ0ZXJfc3RhdChkZW5zaXR5KSksIGFscGhhPTAuMiwgZmlsbCA9ICJwaW5rIiwgCiAgICAgICAgICAgICAgICAgICBjb2xvcj0iZGFya3JlZCIsIHBvc2l0aW9uPSJpZGVudGl0eSIsIGJpbnMgPSA0MCkgKwogICAgdGhlbWVfbWluaW1hbCgpCmBgYAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KI3N0YXRzCmRlc2NyaWJlKG5vcm1fMTAwaywgZmFzdD1UUlVFKSAlPiUgCiAgZHBseXI6OnNlbGVjdChjKC12YXJzLC1uKSkKCiNkaXN0cmlidXRpb24Kbm9ybV8xMDBrICU+JSAKICBnYXRoZXIodmFyaWFibGUsIHZhbHVlLCAxOjYpICU+JQogIGdncGxvdChhZXModmFsdWUpKSArCiAgICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArCiAgICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJncmVlbiIsIGFscGhhPTAuOSwgY29sb3I9ImRhcmtncmVlbiIpICsKICAgIGdlb21faGlzdG9ncmFtKGFlcyh5PWFmdGVyX3N0YXQoZGVuc2l0eSkpLCBhbHBoYT0wLjIsIGZpbGwgPSAicGluayIsIAogICAgICAgICAgICAgICAgICAgY29sb3I9ImRhcmtyZWQiLCBwb3NpdGlvbj0iaWRlbnRpdHkiLCBiaW5zID0gNDApICsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKCgoKCiMjIE1vZGVsCgoKYGBge3J9CnNldC5zZWVkKDc3NykKCnNpbXBfcmVnX3NhbXBsZV8xayA8LSBub3JtXzFrJFRvdGFsLlJldmVudWUgJT4lCiAgY3JlYXRlRGF0YVBhcnRpdGlvbihwID0gMC44LCBsaXN0ID0gRkFMU0UpCnNpbXAxa190cmFpbiAgPC0gbm9ybV8xa1tzaW1wX3JlZ19zYW1wbGVfMWssIF0Kc2ltcDFrX3Rlc3QgPC0gbm9ybV8xa1stc2ltcF9yZWdfc2FtcGxlXzFrLCBdCgoKc2ltcF9yZWdfc2FtcGxlXzEwMGsgPC0gbm9ybV8xMDBrJFRvdGFsLlJldmVudWUgJT4lCiAgY3JlYXRlRGF0YVBhcnRpdGlvbihwID0gMC44LCBsaXN0ID0gRkFMU0UpCnNpbXAxMDBrX3RyYWluICA8LSBub3JtXzEwMGtbc2ltcF9yZWdfc2FtcGxlXzEwMGssIF0Kc2ltcDEwMGtfdGVzdCA8LSBub3JtXzEwMGtbLXNpbXBfcmVnX3NhbXBsZV8xMDBrLCBdCgpgYGAKCgpgYGB7cn0Kc2ltcHRyYWluMWtfbW9kZWw8LSBsbShUb3RhbC5SZXZlbnVlflVuaXRzLlNvbGQsIGRhdGE9c2ltcDFrX3RyYWluICkKCnN1bW1hcnkoc2ltcHRyYWluMWtfbW9kZWwpCiMgTWFrZSBwcmVkaWN0aW9ucwpwcmVkaWN0aW9uIDwtIHNpbXB0cmFpbjFrX21vZGVsICU+JSBwcmVkaWN0KHNpbXAxa190ZXN0KQoKY2xhc3Moc2ltcDFrX3Rlc3QkVG90YWwuUmV2ZW51ZSkKYGBgCgoKYGBge3J9CiMgTW9kZWwgcGVyZm9ybWFuY2UKZGF0YS5mcmFtZSgKICBNQUUgPSBtYWUocHJlZGljdGlvbiwgc2ltcDFrX3Rlc3QkVG90YWwuUmV2ZW51ZSksCiAgUk1TRSA9IFJNU0UocHJlZGljdGlvbiwgc2ltcDFrX3Rlc3QkVG90YWwuUmV2ZW51ZSksCiAgUjIgPSBSMihwcmVkaWN0aW9uLCBzaW1wMWtfdGVzdCRUb3RhbC5SZXZlbnVlKQopCmBgYAoKYGBge3J9CnNpbXB0cmFpbjEwMGtfbW9kZWw8LSBsbShUb3RhbC5SZXZlbnVlflVuaXRzLlNvbGQsIGRhdGE9c2ltcDEwMGtfdHJhaW4gKQoKc3VtbWFyeShzaW1wdHJhaW4xMDBrX21vZGVsKQojIE1ha2UgcHJlZGljdGlvbnMKcHJlZGljdGlvbiA8LSBzaW1wdHJhaW4xMDBrX21vZGVsICU+JSBwcmVkaWN0KHNpbXAxMDBrX3Rlc3QpCgpjbGFzcyhzaW1wMTAwa190ZXN0JFRvdGFsLlJldmVudWUpCmBgYAoKYGBge3J9CiMgTW9kZWwgcGVyZm9ybWFuY2UKZGF0YS5mcmFtZSgKICBNQUUgPSBtYWUocHJlZGljdGlvbiwgc2ltcDEwMGtfdGVzdCRUb3RhbC5SZXZlbnVlKSwKICBSTVNFID0gUk1TRShwcmVkaWN0aW9uLCBzaW1wMTAwa190ZXN0JFRvdGFsLlJldmVudWUpLAogIFIyID0gUjIocHJlZGljdGlvbiwgc2ltcDEwMGtfdGVzdCRUb3RhbC5SZXZlbnVlKQopCmBgYAoKClRoZSBzdGVwcyB0YWtlbiBmb3IgYSBzaW1wbGUgcmVncmVzc2lvbiB3ZXJlIHNwbGl0dGluZyB0aGUgbm9ybWFsaXplZCBkYXRhIGludG8gYSB0cmFpbiBhbmQgdGVzdCBvbmx5IHVzaW5nIHRoZSBudW1lcmljIHZhbHVlcy4gVXNpbmcgYFVuaXRzLlNvbGRgIGFzIHRoZSBwcmVkaWN0b3IgdmFyaWFibGUgSSBydW4gbXkgbW9kZWxzLgpUaGUgUi1zcXVhcmVkIHZhbHVlIG9mIDAuMjgyNiBhbmQgMC4yNzY5IHNob3dzIHRoZXNlIGFyZSB0ZXJyaWJsZSBtb2RlbHMsIGJ1dCB0aGF0IHdhcyBleHBlY3RlZCBmcm9tIHRoZSBFREEuIFRoZSBtb2RlbHMgYWNjdXJhY3kgaXMgYWJvdXQgMjclLTI4JSB3aGljaCBqdXN0IHNob3dzIGl0IHdhcyBub3QgYSBnb29kIG1vZGVsLgoKCiMjIERlY2lzaW9uIFRyZWUKVG8gc2ltcGxpZnkgZGVjaXNpb24gdHJlZSwgdGhlIGFwcHJvYWNoIEkgd2lsbCB1c2UgYSBhdHRyaWJ1dGUgd2l0aCBhIGxvd2VyIG51bWJlciBvZiB1bmlxdWUgdmFsdWVzLCBidXQgSSd2ZSBjaG9zZW4gKipub3QqKiB0byBnbyB3aXRoIFNhbGVzLkNoYW5uZWwsIHNpbmNlIHRoaXMgbW9kZWwgaXMgdmVyeSBtdWNoIHJhbmRvbSBhbmQgSSBob3BlIHRvIGltcGxlbWVudCBhIGRlY2lzaW9uIHRyZWUgd2l0aCBtb3JlIHRoYW4gMiBwb3NzaWJsZSBvdXRjb21lcyBmb3IgYW5hbHlzaXMuIFdpdGggdGhpcyBpbiBtaW5kIEkgd2lsbCBtYWtlIGEgZGVjaXNpb24gdHJlZSBtb2RlbCB1c2luZyBSZWdpb24sIHdoaWNoIEkgYWxyZWFkeSBzdXNwZWN0IHdpbGwgY3JlYXRlIGFuIG91dGNvbWUgd2hlcmUgRXVyb3BlIGFuZCBTdWItU2FoYXJhbiBBZnJpY2EgYXJlIHRoZSBtb3N0IGxpa2VseSB0aGUgcmVnaW9ucyB0aGF0IHdpbGwgYmUgaGlnaGxpZ2h0ZWQgaW4gbXkgZGVjaXNpb24gdHJlZSwgYmVjYXVzZSBvZiBpdHMgaGlnaCBmcmVxdWVuY3kgaW4gdGhlIGRhdGEgZnJhbWVzLiBJIHdpbGwgdXNlIFtycGFydF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3JwYXJ0L3JwYXJ0LnBkZikgZm9yIG15IGRlY2lzaW9uIHRyZWUuCk5PVEU6IHRoaXMgd2lsbCBiZSBteSBmaXJzdCB0aW1lIHVzaW5nIHJwYXJ0LCBzbyBJIGFtIGN1cmlvdXMgb24gdGhlIHJlc3VsdHMuCgoKCgpgYGB7cn0KI3NwbGl0IGludG8gdGVzdC90cmFpbiBzZXQKCiNGb3IgZGZfMWsKc2V0LnNlZWQoMjM0MSkKc2FtcGxlX3NldCA8LSBzYW1wbGUobnJvdyhkZl8xayksIHJvdW5kKG5yb3coZGZfMWspKjAuNzUpLCByZXBsYWNlID0gRkFMU0UpCmRmXzFrX3RyYWluIDwtIGRmXzFrW3NhbXBsZV9zZXQsIF0KZGZfMWtfdGVzdCA8LSBkZl8xa1stc2FtcGxlX3NldCwgXQoKIyBGb3IgZGZfMTAwawoKc2FtcGxlX3NldCA8LSBzYW1wbGUobnJvdyhkZl8xMDBrKSwgcm91bmQobnJvdyhkZl8xMDBrKSowLjc1KSwgcmVwbGFjZSA9IEZBTFNFKQpkZl8xMDBrX3RyYWluIDwtIGRmXzEwMGtbc2FtcGxlX3NldCwgXQpkZl8xMDBrX3Rlc3QgPC0gZGZfMTAwa1stc2FtcGxlX3NldCwgXQoKI2NoZWNrIGNsYXNzIGRpc3RyaWJ1dGlvbiBvZiBvcmlnaW5hbCwgdHJhaW4sIGFuZCB0ZXN0IHNldHMKdGFibGVfMWs8LXJvdW5kKHByb3AudGFibGUodGFibGUoZHBseXI6OnNlbGVjdChkZl8xaywgUmVnaW9uKSwgZXhjbHVkZSA9IE5VTEwpKSwKICAgICAgICAgICAgNCkgKiAxMDAKdGFibGVfMWtfdHJhaW48LXJvdW5kKHByb3AudGFibGUodGFibGUoZHBseXI6OnNlbGVjdChkZl8xa190cmFpbiAsIFJlZ2lvbiksIGV4Y2x1ZGUgPSBOVUxMKSksCiAgICAgICAgICAgIDQpICogMTAwCnRhYmxlXzFrX3Rlc3Q8LXJvdW5kKHByb3AudGFibGUodGFibGUoZHBseXI6OnNlbGVjdChkZl8xa190ZXN0LCBSZWdpb24pLCBleGNsdWRlID0gTlVMTCkpLAogICAgICAgICAgICA0KSAqIDEwMAoKdGFibGVfMTAwazwtcm91bmQocHJvcC50YWJsZSh0YWJsZShkcGx5cjo6c2VsZWN0KGRmXzEwMGssIFJlZ2lvbiksIGV4Y2x1ZGUgPSBOVUxMKSksCiAgICAgICAgICAgIDQpICogMTAwCnRhYmxlXzEwMGtfdHJhaW48LXJvdW5kKHByb3AudGFibGUodGFibGUoZHBseXI6OnNlbGVjdChkZl8xMDBrX3RyYWluLCBSZWdpb24pLCBleGNsdWRlID0gTlVMTCkpLAogICAgICAgICAgICA0KSAqIDEwMAp0YWJsZV8xMDBrX3Rlc3Q8LXJvdW5kKHByb3AudGFibGUodGFibGUoZHBseXI6OnNlbGVjdChkZl8xMDBrX3Rlc3QsIFJlZ2lvbiksIGV4Y2x1ZGUgPSBOVUxMKSksCiAgICAgICAgICAgIDQpICogMTAwCgoKYXMuZGF0YS5mcmFtZSh0YWJsZV8xaykKYXMuZGF0YS5mcmFtZSh0YWJsZV8xa190cmFpbikKYXMuZGF0YS5mcmFtZSh0YWJsZV8xa190ZXN0KQoKYXMuZGF0YS5mcmFtZSh0YWJsZV8xMDBrKQphcy5kYXRhLmZyYW1lKHRhYmxlXzEwMGtfdHJhaW4pCmFzLmRhdGEuZnJhbWUodGFibGVfMTAwa190ZXN0KQoKCmBgYAoKCgoKSW5jb3Jwb3JhdGluZyBgT3JkZXIuSURgIGluIG15IG1vZGVsIGtlcHQgY2F1c2luZyBteSBmaWxlIHRvIGNyYXNoIGRlc3BpdGUgaXQgbm90IGJlaW5nIG1hZGUgaW50byBhIGZhY3RvciwgdGhlcmVmb3JlIEkgb3B0ZWQgdG8gcmVtb3ZlIGl0LCBzbyB0aGF0IEkgbWF5IHNlZSB0aGUgcmVzdWx0cy4KCmBgYHtyfQpkZl8xa190cmFpbjwtZGZfMWtfdHJhaW4lPiUKICAgICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoLWMoT3JkZXIuSUQpKQpgYGAKCgoKYGBge3J9CiNidWlsZCBtb2RlbCB2aWEgcnBhcnQgcGFja2FnZQptb2RlbF8xayA8LSBycGFydChSZWdpb24gfiBVbml0cy5Tb2xkLAogICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImNsYXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkZl8xa190cmFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2w9cnBhcnQuY29udHJvbChtaW5zcGxpdD0xLCBtaW5idWNrZXQ9MSwgY3A9MC4wMDEpCiAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKI2Rpc3BsYXkgZGVjaXNpb24gdHJlZQojIHJwYXJ0LnBsb3QobW9kZWxfMTAwaykKcnBhcnQucGxvdChtb2RlbF8xaywgYm94LnBhbGV0dGUgPSAiQmx1ZXMiKQpgYGAKQmVjYXVzZSB0aGUgZGF0YSBpcyB1bmRlY2lwaGVyYWJsZSBpbiB0aGlzIGZvcm0gSSBvcHRlZCB0byBtYWtlIGEgc2ltcGxlciBvbmUgd2l0aCBqdXN0IGNhdGVnb3JpY2FsIHZhbHVlcy4KCmBgYHtyfQojYnVpbGQgbW9kZWwgdmlhIHJwYXJ0IHBhY2thZ2UKbW9kZWxfMWsgPC0gcnBhcnQoSXRlbS5UeXBlIH4gT3JkZXIuUHJpb3JpdHksCiAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiY2xhc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRmXzFrX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbD1ycGFydC5jb250cm9sKG1pbnNwbGl0PTEsIG1pbmJ1Y2tldD0xLCBjcD0wLjAwMSkKICAgICAgICAgICAgICAgICAgICAgICAgICApCgojZGlzcGxheSBkZWNpc2lvbiB0cmVlCiMgcnBhcnQucGxvdChtb2RlbF8xMDBrKQpycGFydC5wbG90KG1vZGVsXzFrLCBib3gucGFsZXR0ZSA9ICJCbHVlcyIpCmBgYAoKCgpgYGB7cn0KCiNidWlsZCBtb2RlbCB2aWEgcnBhcnQgcGFja2FnZQptb2RlbF8xMDBrIDwtIHJwYXJ0KFJlZ2lvbiB+IC4tQ291bnRyeSwKICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJjbGFzcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGZfMTAwa190cmFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2w9cnBhcnQuY29udHJvbChtaW5zcGxpdD0xLCBtaW5idWNrZXQ9MSwgY3A9MC4wMDEpCiAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKI2Rpc3BsYXkgZGVjaXNpb24gdHJlZQojIHJwYXJ0LnBsb3QobW9kZWxfMTAwaykKcnBhcnQucGxvdChtb2RlbF8xMDBrLCBib3gucGFsZXR0ZSA9ICJCbHVlcyIpCmBgYAoKIyBSZXNwb25zZSB0byBRdWVzdGlvbnMKCiMjIDEuIEFyZSB0aGUgY29sdW1ucyBvZiB5b3VyIGRhdGEgY29ycmVsYXRlZD8KClllcyB0aGV5IHdlcmUuIEl0cyBhcHBhcmVudCBpbiBqdXN0IHRoZSByZWxhdGlvbnNoaXBzIGludm9sdmVkLCBzdWNoIGFzIGBDb3VudHJ5YCBiZWluZyBjYXRlZ29yaXplZCBpbiBgUmVnaW9uYCwgYW5kIHRoZSBudW1lcmljIGF0dHJpYnV0ZXMgd2l0aCBsYWJlbCAiVG90YWwiIGJlaW5nIGRlcml2ZWQgZnJvbSB0aGVpciBjYWxjdWxhdGlvbnMuIEkgYWxzbyBpbW1lZGlhdGVseSBub3RlZCB0aGUgbXVsdGljb2xsaW5lYXJpdHkgd2hpY2ggbWFkZSBpdCBWRVJZIGRpZmZpY3VsdCBvbiBob3cgSSB3YW50ZWQgdG8gcHJvY2VlZC4KCiMjIDIuIEFyZSB0aGVyZSBsYWJlbHMgaW4geW91ciBkYXRhPyBEaWQgdGhhdCBpbXBhY3QgeW91ciBjaG9pY2Ugb2YgYWxnb3JpdGhtPwoKTm8sIGFmdGVyIGNoZWNraW5nIGJvdGggZGF0YSBzZXRzLCBuZWl0aGVyIGhhZCBhbnkgbGFiZWxzLgoKIyMgMy4gV2hhdCBhcmUgdGhlIHByb3MgYW5kIGNvbnMgb2YgZWFjaCBhbGdvcml0aG0geW91IHNlbGVjdGVkPwoKVGhlIFNpbXBsZSBSZWdyZXNzaW9uIG1vZGVsIGhlbHBlZCBpZGVudGlmeSB0aGUgZ2FyYmFnZSBpbiBnYXJiYWdlIG91dCBkYXRhIHJlc3VsdHMgd2Ugd2VyIGdldHRpbmcsIGFuZCBiZWNhdXNlIG9mIG15IGZhbWlsaWFyaXR5IHdpdGggaXQgSSB3YXMgYWJsZSB0byBhc3Nlc3MgYW5kIHVuZGVyc3RhbmQgdGhlIHJlc3VsdHMgdmVyeSBlYXNpbHkuCgpJbiBjb250cmFzdCwgdGhpcyBpcyB0aGUgZmlyc3QgdGltZSBJJ20gdXNpbmcgYSBSZWdyZXNzaW9uIFRyZWUgYW5kIEkgYW0gbm90IDEwMCUgY29tZm9ydGFibGUgd2l0aCBzZWxlY3RpbmcgZGF0YSB0aGF0IGlzIGJlc3QgdXNlZCBmb3IgdGhpcyBtb2RlbC4gRm9yIGluc3RhbmNlLCBvcmlnaW5hbGx5IEkgaGFkIGRlY2lkZWQgdG8gc2VsZWN0IGBSZWdpb25gIGFuZCBgVW5pdHMuU29sZGAgZm9yIG15IHRyZWUsIGJ1dCBgUmAgZGlkIG5vdCBtYWtlIGEgdXNlZnVsIG9mIGV2ZW4gdmlld2FibGUgdmlzdWFsLiBJIGVuZGVkIHVwIHVzaW5nIHRvIHNtYWxsIGNhdGVnb3JpZXMgaW4gdGhlIDFrIGRhdGEgc28gdGhlIHJlc3VsdCB3YXMgcHJpbnRhYmxlLCBidXQgaW4gY29udGVzdCB3aXRoIHRoZSBkYXRhLCBhbGwgSSBjYW4gZGVjaXBoZXIgaXMgYmFzZWQgb24gdGhlIGZyZXF1ZW5jeSB0aGlzIGlzIHRoZSBsaWtlbGlob29kIG9mIGEgbGV2ZWwgb2YgcHJpb3JpdHkgYmFzZWQgb24gYEl0ZW0uVHlwZWAsIHdoaWNoIGlzIHN0aWxsIGEgc29tZXdoYXQgY29uZnVzaW5nIGFzc2Vzc21lbnQgZm9yIG1lLiBJIGFsc28gcmVhZCB0aHJvdWdoIHRoZSBbY3JhbiByX3Byb2plY3Qub3JnIGRvY3VtZW50YXRpb24gZm9yIHJwYXJ0XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvbGFiZWxsZWQvdmlnbmV0dGVzL2ludHJvX2xhYmVsbGVkLmh0bWwpIHRoZWlyIGlzIGxpbWl0YXRpb25zIHRvIHRoZSBhbW91bnQgb2YgZmFjdG9ycyB5b3UgbWF5IHVzZSwgZm9yY2luZyBtZSB0byBkaXNyZWdhcmQgQ291bnRyeSBhbHRvZ2V0aGVyLiBVc2luZyB0aGUgbGFyZ2VyIGRhdGEgc2V0IEkgZmVlbCBhIGdyZWF0IGRlYWwgb2YgZGF0YSB3YXMgb21taXR0ZWQgY29uc2lkZXJzaW5nIG9ubHkgMiByZWdpb25zIHdlcmUgcmVwcmVzZW50ZWQgaGVyZS4KCjQuIEhvdyB5b3VyIGNob2ljZSBvZiBhbGdvcml0aG0gcmVsYXRlcyB0byB0aGUgZGF0YXNldHMgKHdhcyB5b3VyIGNob2ljZSBvZiBhbGdvcml0aG0gaW1wYWN0ZWQgYnkgdGhlIGRhdGFzZXRzIHlvdSBjaG9zZSk/CgpJIGNob3NlIHNpbXBsZSByZWdyZXNzaW9uIHdoZW4gSSBmaWd1cmVkIHRoZSBkYXRhIGhhZCBtdWx0aWNvbGxpbmVhcml0eSBhbmQgYXNzdW1lZCB0aGF0IG15IHRyYW5zZm9ybWF0aW9ucyB3b3VsZCBub3QgZG8gbXVjaCB0byBtYWtlIHRoZSBkYXRhIGEgYmV0dGVyIGZpdC4KCjUuIFdoaWNoIHJlc3VsdCB3aWxsIHlvdSB0cnVzdCBpZiB5b3UgbmVlZCB0byBtYWtlIGEgYnVzaW5lc3MgZGVjaXNpb24/CgpTaW1wbGUgcmVncmVzc2lvbi4gSSB3b3VsZCBoYXZlIHRvIGNpcmNsZSBiYWNrIHRvIGJ1c2luZXNzIGFuZCBleHBsYWluIHdoeSB0aGUgZGF0YSB3b3VsZCBub3QgYmUgYSBzdWl0YWJsZSBmaXQgZm9yIHByZWRpY3Rpb24gb3IgYW5hbHlzaXMuCgo2LiBEbyB5b3UgdGhpbmsgYW4gYW5hbHlzaXMgY291bGQgYmUgcHJvbmUgdG8gZXJyb3JzIHdoZW4gdXNpbmcgdG9vIG11Y2ggZGF0YSwgb3Igd2hlbiB1c2luZyB0aGUgbGVhc3QgYW1vdW50IHBvc3NpYmxlPwoKRGVmaW5pdGVseSB0aGUgRGVjaXNpb24gVHJlZSwgYnV0IHRvIGJlIGZyYW5rIG9wZXJhdG9yIGVycm9ycyBhbmQgdW5mYW1pbGlhcml0eSB3aXRoIHRoaXMgbWV0aG9kIGlzIGRlZmluaXRlbHkgYSBtYWpvciBmYWN0b3IgdG8gYWNjb3VudCBmb3IuCgo3LiBIb3cgZG9lcyB0aGUgYW5hbHlzaXMgYmV0d2VlbiBkYXRhIHNldHMgY29tcGFyZT8KCk5vLiBBZnRlciBJIGFzc2VzcyB0aGUgbGFjayBvZiB1c2VmdWxuZXNzIG9mIHRoZSBudW1lcmljIHZhbHVlcyBJIG9wdGVkIHRvIG1ha2UgdGhpcyBhIGxlYXJuaW5nIG9wcG9ydHVuaXR5IGluIHVzaW5nIGEgZGVjaXNpb24gdHJlZSBhbmQgZmFtaWxpYXJpemluZyBteXNlbGYgd2l0aCBpdCBmb3IgZnV0dXJlIHVzZS4KCmBgYHtyfQpybShsaXN0ID0gbHMocGF0dGVybiA9ICJfdG1wJCIpKQpgYGAKCgo=