Calculating the sample mean

The late_shipments dataset contains supply chain data on the delivery of medical supplies. Each row represents one delivery of a part. The late columns denotes whether or not the part was delivered late. A value of "Yes" means that the part was delivered late, and a value of "No" means the part was delivered on time.

Let’s begin our analysis by calculating a point estimate (sample statistic), namely the proportion of late shipments.

# Load libraries
library(tidyr)
library(dplyr)

Attaching package: ‘dplyr’

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

    filter, lag

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

    intersect, setdiff, setequal, union
library(ggplot2)
library(fst)
fst package v0.9.8
library(tibble)
library(readr)
path_ltshp <- "C:/Users/JuanFer Mosquera/Documents/datasets/late_shipments.fst"
late_shipments <- read_fst(path_ltshp)
fstcore package v0.9.18
(OpenMP detected, using 20 threads)
glimpse(late_shipments)
Rows: 1,000
Columns: 26
$ id                       <dbl> 73003, 41222, 52354, 28471, 16901, 27238, 9100, 7183, 16784, 75595, 78576, 23957, 47937, 16232, 32402, 19263, 1618, 20264, 4123…
$ country                  <chr> "Vietnam", "Kenya", "Zambia", "Nigeria", "Vietnam", "Sudan", "Nigeria", "Rwanda", "Vietnam", "Haiti", "C\xaate d'Ivoire", "Viet…
$ managed_by               <chr> "PMO - US", "PMO - US", "PMO - US", "PMO - US", "PMO - US", "PMO - US", "PMO - US", "PMO - US", "PMO - US", "PMO - US", "PMO - …
$ fulfill_via              <chr> "Direct Drop", "Direct Drop", "Direct Drop", "Direct Drop", "Direct Drop", "Direct Drop", "Direct Drop", "Direct Drop", "Direct…
$ vendor_inco_term         <chr> "EXW", "EXW", "EXW", "EXW", "EXW", "EXW", "EXW", "EXW", "EXW", "EXW", "EXW", "EXW", "EXW", "EXW", "EXW", "EXW", "EXW", "EXW", "…
$ shipment_mode            <chr> "Air", "Air", "Air", "Air", "Air", "Air", "Air", "Air", "Air", "Air", "Air", "Air", "Air", "Air", "Air", "Air", "Air", "Air", "…
$ late_delivery            <dbl> 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ late                     <chr> "No", "No", "No", "Yes", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", …
$ product_group            <chr> "ARV", "HRDT", "HRDT", "HRDT", "ARV", "HRDT", "HRDT", "HRDT", "ARV", "MRDT", "HRDT", "ARV", "ARV", "ARV", "ARV", "HRDT", "HRDT"…
$ sub_classification       <chr> "Adult", "HIV test", "HIV test", "HIV test", "Adult", "HIV test", "HIV test", "HIV test", "Adult", "Malaria", "HIV test", "Adul…
$ vendor                   <chr> "HETERO LABS LIMITED", "Orgenics, Ltd", "Orgenics, Ltd", "Orgenics, Ltd", "Aurobindo Pharma Limited", "Trinity Biotech, Plc", "…
$ item_description         <chr> "Efavirenz/Lamivudine/Tenofovir Disoproxil Fumarate 600/300/300mg, tablets, 30 Tabs", "HIV 1/2, Determine Complete HIV Kit, 100…
$ molecule_test_type       <chr> "Efavirenz/Lamivudine/Tenofovir Disoproxil Fumarate", "HIV 1/2, Determine Complete HIV Kit", "HIV 1/2, Determine Complete HIV K…
$ brand                    <chr> "Generic", "Determine", "Determine", "Determine", "Generic", "Uni-Gold", "Determine", "First Response", "Generic", "Generic", "…
$ dosage                   <chr> "600/300/300mg", "N/A", "N/A", "N/A", "300mg", "N/A", "N/A", "N/A", "300mg", "N/A", "N/A", "600/300/300mg", "600/300/300mg", "5…
$ dosage_form              <chr> "Tablet - FDC", "Test kit", "Test kit", "Test kit", "Tablet", "Test kit", "Test kit", "Test kit", "Tablet", "Test kit", "Test k…
$ unit_of_measure_per_pack <dbl> 30, 100, 100, 100, 60, 20, 100, 30, 30, 25, 100, 30, 30, 30, 240, 100, 20, 1, 100, 1, 25, 30, 60, 180, 30, 20, 30, 60, 30, 100,…
$ line_item_quantity       <dbl> 19200, 6100, 1364, 2835, 112, 53, 1500, 6180, 34640, 4, 3500, 19200, 24192, 2894, 1905, 1500, 9000, 2988, 32, 1600, 15304, 3715…
$ line_item_value          <dbl> 201600.00, 542900.00, 109120.00, 252315.00, 1618.40, 1696.00, 120000.00, 101970.00, 122972.00, 45.00, 245000.00, 201600.00, 254…
$ pack_price               <dbl> 10.50, 89.00, 80.00, 89.00, 14.45, 32.00, 80.00, 16.50, 3.55, 11.25, 70.00, 10.50, 10.50, 1.25, 1.43, 80.00, 32.00, 24.50, 25.0…
$ unit_price               <dbl> 0.35, 0.89, 0.80, 0.89, 0.24, 1.60, 0.80, 0.55, 0.12, 0.45, 0.70, 0.35, 0.35, 0.04, 0.01, 0.80, 1.60, 24.50, 0.25, 24.50, 0.80,…
$ manufacturing_site       <chr> "Hetero Unit III Hyderabad IN", "Alere Medical Co., Ltd.", "Alere Medical Co., Ltd.", "Alere Medical Co., Ltd.", "Aurobindo Uni…
$ first_line_designation   <chr> "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "…
$ weight_kilograms         <dbl> 2719, 3497, 553, 1352, 1701, 18, 445, 2130, 2804, 2, 794, 2877, 3385, 1486, 786, 714, 9000, 1992, 68, 1111, 5102, 385, 4007, 14…
$ freight_cost_usd         <dbl> 4084.56, 40917.19, 7845.09, 31284.02, 4288.83, 569.11, 7120.00, 17331.66, 9759.55, 702.42, 12506.12, 4312.92, 8764.53, 12749.39…
$ line_item_insurance_usd  <dbl> 207.24, 895.78, 112.18, 353.75, 2.67, 2.80, 235.20, 163.15, 152.12, 0.06, 528.22, 207.24, 314.22, 5.07, 2.80, 198.00, 460.80, 9…
late_shipments
colnames(late_shipments)
 [1] "id"                       "country"                  "managed_by"               "fulfill_via"              "vendor_inco_term"        
 [6] "shipment_mode"            "late_delivery"            "late"                     "product_group"            "sub_classification"      
[11] "vendor"                   "item_description"         "molecule_test_type"       "brand"                    "dosage"                  
[16] "dosage_form"              "unit_of_measure_per_pack" "line_item_quantity"       "line_item_value"          "pack_price"              
[21] "unit_price"               "manufacturing_site"       "first_line_designation"   "weight_kilograms"         "freight_cost_usd"        
[26] "line_item_insurance_usd" 
summary(late_shipments)
       id          country           managed_by        fulfill_via        vendor_inco_term   shipment_mode      late_delivery       late          
 Min.   :   92   Length:1000        Length:1000        Length:1000        Length:1000        Length:1000        Min.   :0.000   Length:1000       
 1st Qu.:19492   Class :character   Class :character   Class :character   Class :character   Class :character   1st Qu.:0.000   Class :character  
 Median :39631   Mode  :character   Mode  :character   Mode  :character   Mode  :character   Mode  :character   Median :0.000   Mode  :character  
 Mean   :40308                                                                                                  Mean   :0.067                     
 3rd Qu.:63148                                                                                                  3rd Qu.:0.000                     
 Max.   :82005                                                                                                  Max.   :1.000                     
 product_group      sub_classification    vendor          item_description   molecule_test_type    brand              dosage          dosage_form       
 Length:1000        Length:1000        Length:1000        Length:1000        Length:1000        Length:1000        Length:1000        Length:1000       
 Class :character   Class :character   Class :character   Class :character   Class :character   Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
                                                                                                                                                        
                                                                                                                                                        
                                                                                                                                                        
 unit_of_measure_per_pack line_item_quantity line_item_value     pack_price        unit_price     manufacturing_site first_line_designation weight_kilograms  
 Min.   :   1.00          Min.   :     1     Min.   :      0   Min.   :   0.00   Min.   : 0.000   Length:1000        Length:1000            Min.   :     1.0  
 1st Qu.:  30.00          1st Qu.:   450     1st Qu.:  10067   1st Qu.:   6.85   1st Qu.: 0.110   Class :character   Class :character       1st Qu.:   136.2  
 Median :  60.00          Median :  2744     Median :  62318   Median :  24.50   Median : 0.485   Mode  :character   Mode  :character       Median :   844.0  
 Mean   :  82.06          Mean   : 14291     Mean   : 151129   Mean   :  39.93   Mean   : 1.366                                             Mean   :  2102.0  
 3rd Qu.: 100.00          3rd Qu.: 10000     3rd Qu.: 219520   3rd Qu.:  70.05   3rd Qu.: 0.890                                             3rd Qu.:  2403.0  
 Max.   :1000.00          Max.   :333334     Max.   :2801262   Max.   :1242.53   Max.   :24.850                                             Max.   :154780.0  
 freight_cost_usd    line_item_insurance_usd
 Min.   :    14.36   Min.   :   0.00        
 1st Qu.:  1899.72   1st Qu.:  14.58        
 Median :  5886.93   Median :  95.48        
 Mean   : 11342.15   Mean   : 232.86        
 3rd Qu.: 15533.10   3rd Qu.: 329.22        
 Max.   :289653.20   Max.   :4939.20        

Explore N/A values

late_shipments <- late_shipments %>%
 mutate(shipment_mode = na_if(shipment_mode, "N/A"))
# Count NA values in each column
na_counts <- colSums(is.na(late_shipments))

# Sort the results in descending order
sorted_na_counts <- sort(na_counts, decreasing = TRUE)

# See the result
sorted_na_counts
           shipment_mode                       id                  country               managed_by              fulfill_via         vendor_inco_term 
                       1                        0                        0                        0                        0                        0 
           late_delivery                     late            product_group       sub_classification                   vendor         item_description 
                       0                        0                        0                        0                        0                        0 
      molecule_test_type                    brand                   dosage              dosage_form unit_of_measure_per_pack       line_item_quantity 
                       0                        0                        0                        0                        0                        0 
         line_item_value               pack_price               unit_price       manufacturing_site   first_line_designation         weight_kilograms 
                       0                        0                        0                        0                        0                        0 
        freight_cost_usd  line_item_insurance_usd 
                       0                        0 
as.factor(late_shipments$vendor_inco_term)

Testing the hypotheses

# Calculate the proportion of late shipments
late_prop_samp <- late_shipments %>%
  summarise(prop_late_shipments = mean(late == "Yes")) %>%
  pull(prop_late_shipments)

# see the results
late_prop_samp
[1] 0.067

Calculating a z-score

Since variables have arbitrary ranges and units, we need to standardize them. For example, it would be silly if a hypothesis test gave a different answer if your variables were in Euros instead of US dollars. Standardization avoids that.

One standardized value of interest in a hypothesis test is called a z-score. To calculate it, we need three numbers: the sample statistic (point estimate), the hypothesized statistic, and the standard error of the statistic (which we estimate from the bootstrap distribution).

late_shipments_boot_sample <- replicate(
  n = 5000,
  expr = {
    late_shipments %>%
      slice_sample(prop = 1, replace = TRUE) %>%
      summarise(prop_late_shpmt = mean(late == "Yes")) %>%
      pull(prop_late_shpmt)
  }
)
head(late_shipments_boot_sample)
[1] 0.072 0.081 0.064 0.065 0.062 0.075

Generate the bootstrap distribution

late_shipments_boot_distn <- tibble(late_prop = late_shipments_boot_sample) %>%
  mutate(replicate = row_number())
late_shipments_boot_distn
nrow(late_shipments_boot_distn)
[1] 5000
# Hypothesize that the proportion is 6%
late_prop_hyp <- 0.06

# Calculate the standard error
std_error <- late_shipments_boot_distn %>%
  summarise(sd = sd(late_prop)) %>%
  pull(sd)

# Find z-score of late_prop_samp
z_score <- (late_prop_samp - late_prop_hyp) / std_error

# See the results
z_score
[1] 0.8872453

Calculating p-values

In order to determine whether to choose the null hypothesis or the alternative hypothesis, you need to calculate a p-value from the z-score.

Let’s return to the late shipments dataset and the proportion of late shipments.

The null hypothesis, H0, is that the proportion of late shipments is 6%

The alternative hypothesis, Ha, is that the proportion of late shipments is greater than 6%

# Calculate the z-score of late_prop_samp
z_score <- (late_prop_samp - late_prop_hyp) / std_error

# Calculate the p-value
p_value <- pnorm(z_score, lower.tail = FALSE)

# See the result 
p_value
[1] 0.1874734

Calculating confidence intervals

If you give a single estimate of a sample statistic, you are bound to be wrong by some amount. For example, the hypothesized proportion of late shipments was 6%. Even if evidence suggests the null hypothesis that the proportion of late shipments is equal to this, for any new sample of shipments, the proportion is likely to be a little different. Consequently, it’s a good idea to state a confidence interval. That is, you say “we are 95% ‘confident’ the proportion of late shipments is between A and B” (for some value of A and B).

Here, you’ll use quantiles of the bootstrap distribution to calculate the confidence interval.

conf_int_quantile <- late_shipments_boot_distn %>%
  summarise(lower = quantile(late_prop, 0.025), 
            upper = quantile(late_prop, 0.975))

# See the result
conf_int_quantile

Two sample mean test statistic

The hypothesis test for determining if there is a difference between the means of two populations uses a different type of test statistic to the z-scores. It’s called “t”, and can be calculated from three values from each sample using this equation.\[ t = \frac{(\bar{x}_{No} \ - \bar{x}_{Yes})}{\sqrt{\frac{s^2_{No}}{n_{No}} \ + \frac{s^2_{Yes}}{n_{Yes}}}}\]

While trying to determine why some shipments are late, you may wonder if the weight of the shipments that were late is different from the weight of the shipments that were on time. The late_shipments dataset has been split into a “yes” group, where late == "Yes" and a “no” group where late == "No". The weight of the shipment is given in the weight_kilograms variable.

grp_tests <- late_shipments %>%
  group_by(late) %>%
  summarise(x_bar = mean(weight_kilograms), s = sd(weight_kilograms), n = n())

# See the results
grp_tests
# Calculate the numerator of the test statistic
numerator <- grp_tests$x_bar[1] - grp_tests$x_bar[2]

# Calculate the denominator of the test statistic
denominator <- sqrt((grp_tests$s[1]^2 / grp_tests$n[1]) + (grp_tests$s[2]^2 / grp_tests$n[2])) 

# Calculate the test statistics 
tstat <- numerator / denominator

# See the result
tstat
[1] -0.872321

From t to p

Previously, you calculated the test statistic for the two-sample problem of whether the mean weight of shipments is lower for shipments that weren’t late (late == "No") compared to shipments that were late (late == "Yes"). In order to make decisions about it, you need to transform the test statistic with a cumulative distribution function to get a p-value.

Recall the hypotheses:

\(H_{0}\): The mean weight of shipments that weren’t late is the same as the mean weight of shipments that were late.

\(H_{A}\): The mean weight of shipments that weren’t late is less than the mean weight of shipments that were late.

# Calculate the degrees of freedom
degreesof_freedom <- (grp_tests$n[1] + grp_tests$n[2]) - 2

# Calculate the p-value fro, the test stat 
pvalue <- pt(tstat, df = degreesof_freedom, lower.tail = FALSE)

# See the result
pvalue
[1] 0.8083785

Visualizing many categories

So far, we’ve only considered the case of differences in a numeric variable between two categories. Of course, many datasets contain more categories. Before you get to conducting tests on many categories, it’s often helpful to perform exploratory data analysis. That is, calculating summary statistics for each group and visualizing the distributions of the numeric variable for each category using box plots.

Here, we’ll see how the price of each package (pack_price) varies between the three shipment modes (shipment_mode): "Air", "Air Charter", and "Ocean".

# Using late_shipments, group by shipment_mode, and calculate the mean and std dev of pack price
late_shipments %>%
  filter(!is.na(shipment_mode)) %>%
  group_by(shipment_mode) %>%
  summarise(xbar_pack_price = mean(pack_price, na.rm = TRUE), s_pack_price = sd(pack_price, na.rm = TRUE))
unique(late_shipments$shipment_mode)
[1] "Air"         "Ocean"       "Air Charter" NA           
# Using late shipments, plot pack_price vs. shipment_mode as a box plot with flipped x and y coordinates
late_shipments %>%
  filter(!is.na(shipment_mode)) %>%
  ggplot(aes(shipment_mode, pack_price)) +
  geom_boxplot() + 
  coord_flip()

Conducting an ANOVA test

The box plots made it look like the distribution of pack price was different for each of the three shipment modes. However, it didn’t tell us whether the mean pack price was different in each category. To determine that, we can use an ANOVA test. The null and alternative hypotheses can be written as follows.

\(H_{0}\) : Pack prices for every category of shipment mode are the same.

\(H_{A}\) : Pack prices for some categories of shipment mode are different.

We’ll set a significance level of 0.1

# Run a linear regression of pack_price vs. shipment_mode
mdl_pack_pice_vs_shipment_mode <- lm(pack_price ~ shipment_mode, data = late_shipments)

# See the results 
summary(mdl_pack_pice_vs_shipment_mode)

Call:
lm(formula = pack_price ~ shipment_mode, data = late_shipments)

Residuals:
    Min      1Q  Median      3Q     Max 
 -43.15  -34.56  -12.33   26.95 1199.38 

Coefficients:
                         Estimate Std. Error t value Pr(>|t|)    
(Intercept)                43.145      2.086  20.678  < 2e-16 ***
shipment_modeAir Charter  -39.752     25.766  -1.543    0.123    
shipment_modeOcean        -35.330      7.174  -4.925 9.88e-07 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 62.91 on 996 degrees of freedom
  (1 observation deleted due to missingness)
Multiple R-squared:  0.02572,   Adjusted R-squared:  0.02376 
F-statistic: 13.15 on 2 and 996 DF,  p-value: 2.317e-06
# Perform ANOVA on the regression model
anova(mdl_pack_pice_vs_shipment_mode)
Analysis of Variance Table

Response: pack_price
               Df  Sum Sq Mean Sq F value    Pr(>F)    
shipment_mode   2  104042   52021  13.146 2.317e-06 ***
Residuals     996 3941466    3957                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Pairwise t-tests

The ANOVA test didn´t tell us which categories of shipment mode had significant differences in pack_prices. To pinpoint which categories had differences, we could instead use pairwise t-tests.

# Perform pairwise t-tests on pack price, grouped by shipment_mode, no p-value adjustment.
test_results <- pairwise.t.test(late_shipments$pack_price, late_shipments$shipment_mode, p.adjust.method = "none")

# See the results
test_results

    Pairwise comparisons using t tests with pooled SD 

data:  late_shipments$pack_price and late_shipments$shipment_mode 

            Air     Air Charter
Air Charter 0.12    -          
Ocean       9.9e-07 0.87       

P value adjustment method: none 
# Modify the pairwise t-tests to use Bonferroni p-value adjustment
test_results <- pairwise.t.test(late_shipments$pack_price, late_shipments$shipment_mode, p.adjust.method = "bonferroni")

# See the results 
test_results

    Pairwise comparisons using t tests with pooled SD 

data:  late_shipments$pack_price and late_shipments$shipment_mode 

            Air   Air Charter
Air Charter 0.37  -          
Ocean       3e-06 1.00       

P value adjustment method: bonferroni 

Test of tho proportions

You may wonder if the amount paid for freight affects whether or not the shipment was late. Recall that in late_shipments dataset, whether or not the shipment was late is stored in the late column. Freight costs are stored in the freight_cost_group column, and the categories are "expensive" and "reasonable".

We can form the hypotheses to test:

\[ H_{0}: late_{expensive} \ - late_{reasonable} \ = 0 \\ H_{A}: late_{expensive} \ - late_{reasonable} \ > 0 \]

p_hats contains the estimates of population proportions (sample proportions) for the “expensive” and “reasonable” groups. ns contains the sample sizes for these groups.

Calculating proportions & sample sizes

\(late_{expensive} \ and \ late_{reasonable}\)

late_shipments <- late_shipments %>%
  mutate(freight_cost_group = ifelse(freight_cost_usd > 5000, "expensive", "reasonable"))
prop_abs_yes <- late_shipments %>%
  count(late, freight_cost_group) %>%
  mutate(abs_prop = n / nrow(late_shipments)) %>%
  filter(late == "Yes" & (freight_cost_group == "reasonable" | freight_cost_group == "expensive")) %>%
  select(abs_prop)

prop_abs_yes
p_hats <- as.vector(prop_abs_yes$abs_prop)
names(p_hats) <- c("expensive", "reasonable")
p_hats
 expensive reasonable 
     0.052      0.015 
smpl_size <- late_shipments %>%
  count(freight_cost_group) %>%
  mutate(count = n) %>%
  #filter(freight_cost_group == "expensive") %>%
  select(count)

smpl_size
ns <- as.vector(smpl_size$count)
names(ns) <- c("expensive", "reasonable")
ns
 expensive reasonable 
       541        459 

Test of two proportions

We may wonder if the amount paid for freight affects whether or not the shipment was late. Recall that in late_shipments dataset, whether or not the shipment was late is stored in the late column. Freight costs are stored in the freight_cost_group column, and the categories are "expensive" and "reasonable".

# Calculate the pooled estimate of the population proportion
p_hat <- weighted.mean(p_hats, ns)

# See the result
p_hat
[1] 0.035017
# Calculate sample prop´n times one minus sample prop´n
p_hat_times_not_p_hat <- p_hat * (1 - p_hat)

# Divide this by the sample sizes 
p_hat_times_not_p_hat_over_ns <- p_hat_times_not_p_hat / ns

# Calculate std. error
std_error <- sqrt(sum(p_hat_times_not_p_hat_over_ns))

# See the result 
std_error
[1] 0.01166526
# Calculate the z-score 
z_score <- (p_hats["expensive"] - p_hats["reasonable"]) / std_error

# See the result
z_score
expensive 
 3.171812 

prop_test() for two samples

That took a lot of effort to calculate the p-value, so while it is useful to see how the calculations work, it isn’t practical to do in real-world analyses. For daily usage, it’s better to use the infer package.

Recall the hypotheses.\[ H_{0}: late_{expensive} \ - late_{reasonable} \ = 0 \\ H_{A}: late_{expensive} \ - late_{reasonable} \ > 0 \]

Using the late_shipments dataset, use prop_test() to perform a proportion test appropriate to the hypotheses.

library(infer)
# Perform a proportion test appropiate to the hypothesis

test_results <- late_shipments %>%
  prop_test(late ~ freight_cost_group, order = c("expensive", "reasonable"), success = "Yes",
            alternative = "greater", correct = FALSE)

# See the results 
test_results

Performing a chi-square test

The chi-square independence test compares proportions of successes of a categorical variable across categories of another categorical variable.

Trade deals often use a form of business shorthand in order to specify the exact details of their contract. These are International Chamber of Commerce (ICC) international commercial terms, or incoterms for short.

The late_shipments dataset includes a vendor_inco_term that describes the incoterms that applied to a given shipment. The choices are:

Perhaps the incoterms affect whether or not the freight costs are expensive. Test these hypotheses with a significance level of 0.01.

\(H_{0}\) : vendor_inco_term and freight_cost_group are independent

\(H_{A}\) : vendor_inco_term and freight_cost_group are associated

# Plot vendor_inco_term filled by freight_cost_group 
# Make it a proportional stacked bar plot
ggplot(late_shipments, aes(vendor_inco_term, fill = freight_cost_group)) + 
  geom_bar(position = "fill") + 
  ylab("proportion")

# Perform a chi-square test of independence on freight_cost_group and vendor_inco_term 
test_results_so <- late_shipments %>%
  chisq_test(vendor_inco_term ~ freight_cost_group)
Warning: Chi-squared approximation may be incorrect
# See results
test_results_so

Reject the null hypothesis

Visualizing goodness of fit

The chi-square goodness of fit test compares proportions of each level of a categorical variable to hypothesized values. Before running such a test, it can be helpful to visually compare the distribution in the sample to the hypothesized distribution.

Recall the vendor incoterms in the late_shipments dataset. Let’s hypothesize that the four values occur with these frequencies in the population of shipments.

# Using late_shipments,m count the vendor incoterms
vendor_inco_term_counts <- late_shipments %>%
  count(vendor_inco_term)

vendor_inco_term_counts
# Get the number of rows in the whole sample
n_total <- nrow(late_shipments)
n_total
[1] 1000
hypothesized_incot <- tribble(~ vendor_inco_term, ~ prop, "EXW", 0.75, "CIP", 0.05, "CIF", 0.0, "DAP", 0.0, "DDU", 0.0, "DDP", 0.1, "FCA", 0.1) %>%
  # Add a column of hypothesized counts for the incoterms
  mutate(n = prop * n_total)

hypothesized_incot
# Using vendor_inco_term_counts, plot n vs. vendor_inco_term 
ggplot(vendor_inco_term_counts, aes(vendor_inco_term, n)) +
  # Make it a (precalculated) bar plot
  geom_col() +
  # Add points from hypothesized 
  geom_point(data=hypothesized_incot, color = "green")

Performing a goodness of fit test

The bar plot of vendor_inco_term suggested that its distribution across the four categories was quite close to the hypothesized distribution. You’ll need to perform a chi-square goodness of fit test to see whether the differences are statistically significant.

To decide which hypothesis to choose, we’ll set a significance level of 0.1.

hypothesized_props <- c("EXW" = 0.75, "CIP" = 0.05, "CIF"= 0.0, "DAP" = 0.0, "DDU" = 0.0, "DDP"= 0.1, "FCA" = 0.1)

hypothesized_props
 EXW  CIP  CIF  DAP  DDU  DDP  FCA 
0.75 0.05 0.00 0.00 0.00 0.10 0.10 
# Run chi-square goodness of fit test on vendor_inco_term
test_results <- late_shipments %>%
  chisq_test(response = vendor_inco_term, p = hypothesized_props)
Warning: Chi-squared approximation may be incorrect
test_results

Non-Parametric tests

Assumptions in hypothesis testing

Each hypothesis test makes assumptions about the data. It’s only when these assumptions are met that it is appropriate to use that hypothesis test.

Randomness

Whether it uses one or two samples, every hypothesis test assumes that each sample is randomly sourced from its population. If you don’t have a random sample, then it won’t be representative of the population. To check this assumption, you need to know where your data came from. There are no statistical or coding tests you can perform to check this. If in doubt, ask the people involved in collecting the data, or a domain expert that understands the population being sampled.

Independence of observations

Tests also assume that each observation is independent. There are some special cases like paired t-tests where dependencies between two samples are allowed, but these change the calculations so you need to understand where such dependencies occur. As you saw with the paired t-test, not accounting for dependencies results in an increased chance of false negative and false positive errors. This is also a difficult problem to diagnose after you have the data. It needs to be discussed before data collection.

Large sample size

Hypothesis tests also assume that your sample is big enough. Smaller samples incur greater uncertainty, and mean that the Central Limit Theorem doesn’t apply, which in turn means that the sampling distribution might not be normally distributed. The increased uncertainty means you get wider confidence intervals on the parameter you are trying to estimate. The Central Limit Theorem not applying means the calculations on the sample could be nonsense, which increases the chance of false negative and positive errors. The check for “big enough” depends on the test and that’s where we’ll head next.

Large sample size: t-test

For one sample t-tests, a popular heuristic is that you need at least thirty observations in your sample. For the two sample case or ANOVA, you need thirty observations in each. That means you can’t compensate for one small group sample by making the other one bigger. In the paired case, you need thirty pairs of observations.

1 Sometimes you can get away with less than 30; the important thing is that the null distribution appears normal.

One sample

  • At least 30 observations in the sample

\(n \geq 30\)

Paired samples

  • At least 30 pairs of observations across the samples

\(Numbers \ of \ rows \ in \ your \ data \geq 30\)

Two samples

  • At least 30 observations in each sample

\(n_{1} \geq 30 \ , n_{2} \geq 30\)

ANOVA

  • At least pairs of 30 observations in each sample

\(n_{i} \geq 30 for \ all \ values \ of \ i\)

Large sample size: proportion tests

For one sample proportion tests, the sample is considered big enough if it contains at least ten successes and ten failures. Notice that if the probability of success is close to zero or close to one, then you need a bigger sample. In the two sample case the size requirements apply to each sample separately.

One sample

  • Number of successes in sample is greater than or equal to 10.

\(n \times \hat{p} \geq 10\)

  • Number of failures in sample is greater than or equal to 10.

\(n \times (1 - \hat{p}) \geq 10\)

\(n:\) Sample size

\(\hat{p}:\) Proportion of successes on sample

Two samples

  • Number of successes in each sample is greater than or equal to 10.

\(n_{1} \times \hat{p}_{1} \geq 10\)

\(n_{2} \times \hat{p}_{2} \geq 10\)

  • Number of failures in each sample is greater than or equal to 10

\(n_{1} \times (1 - \hat{p}_{1}) \geq 10\)

\(n_{2} \times (1 - \hat{p}_{2}) \geq 10\)

Large sample size: chi-square tests

The chi-square test is slightly more forgiving and only requires five successes and failures in each group rather than ten.

  • The number of successes in each group is greateer than or equal to 5

\(n_{i} \times \hat{p}_{i} \geq 5\) for all values in \(i\)

  • The number of failures in each group is greater than or equal to 5

\(n_{i} \times (1 - \hat{p}_{i}) \geq 5\) for all values in \(i\)

\(n_{i}:\) sample size for group \(i\)

\(\hat{p}_{i}:\) proportion of successes in sample group \(i\)

Sanity check

One more check you can perform is to calculate a bootstrap distribution and visualize it with a histogram. If you don’t see a bell-shaped normal curve, then one of the assumptions hasn’t been met. In that case, you should revisit the data collection process, and see if any of the three assumptions of randomness, independence, and sample size do not hold.

Sepecifying and hypothesizing

 Recall the hypotheses.

\[ H_{0}: late_{expensive} \ - late_{reasonable} \ = 0 \\ H_{A}: late_{expensive} \ - late_{reasonable} \ > 0 \]

Let’s compare that traditional approach using prop_test() with a simulation-based infer pipeline.

# Specify thet we are interested in late proportions across freight_cost_groups, where "Yes" denotes success
specified <- late_shipments %>%
  specify(late ~ freight_cost_group, success = "Yes")

# See the result
specified
Response: late (factor)
Explanatory: freight_cost_group (factor)
# Extend the pipeline to declare the null hypothesis that the variables are independent
hypothesized <- late_shipments %>%
  specify(late ~ freight_cost_group, success = "Yes") %>%
  hypothesize(null = "independence")

# See the result
hypothesized
Response: late (factor)
Explanatory: freight_cost_group (factor)
Null Hypothesis: independence

Generating & calculating

The infer pipeline for hypothesis testing requires four steps to calculate the null distribution: specify, hypothesize, generate, and calculate.

Let’s continue the pipeline you began in the previous coding exercise. We’ll get a set of differences in proportions that are distributed as though the null hypothesis, that the proportion of late shipments is the same across freight cost groups, is true.

# Extend the pipeline to generate 2000 permutations
generated <- late_shipments %>%
  specify(late ~ freight_cost_group, success = "Yes") %>%
  hypothesize(null = "independence") %>%
  generate(reps = 2000, type = "permute")

# See the results 
generated
Response: late (factor)
Explanatory: freight_cost_group (factor)
Null Hypothesis: independence
# Extend the pipeline to caculate the difference in proportions (expensive minus reasonable)
null_distn <- late_shipments %>%
  specify(late ~ freight_cost_group, success = "Yes") %>%
  hypothesize(null = "independence") %>%
  generate(reps = 2000, type = "permute") %>%
  calculate(stat = "diff in props", order = c("expensive", "reasonable"))

null_distn
Response: late (factor)
Explanatory: freight_cost_group (factor)
Null Hypothesis: independence
visualize(null_distn)

Observed statistic and p-value

You now have a null distribution. In order to get a p-value and weigh up the evidence against the null hypothesis, you need to calculate the difference in proportions that is observed in the late_shipments sample.

# Copy, paste & modify the pipeline to get the observed statistic
obs_stat <- late_shipments %>%
  specify(late ~ freight_cost_group, success = "Yes") %>%
  hypothesize(null = "independence") %>%
  calculate(stat = "diff in props", order = c("expensive", "reasonable"))
Message: The independence null hypothesis does not inform calculation of the observed statistic (a difference in proportions) and will be ignored.
# See the results
obs_stat
Response: late (factor)
Explanatory: freight_cost_group (factor)
Null Hypothesis: independence
# Visualize the null dist´n, adding a vertical line to the observed statistic
visualize(null_distn) + 
  geom_vline(aes(xintercept = stat), data=obs_stat, color = "red")

# Get the p-value
p_value <- get_p_value(null_distn, obs_stat, direction = "two sided")
Warning: Please be cautious in reporting a p-value of 0. This result is an approximation based on the number of `reps` chosen in the `generate()` step.
ℹ See ]8;;ide:help:infer::get_p_value`get_p_value()`]8;; for more information.
# See the result
p_value

Simulation-based t-test

W manually performed the steps for a t-test to explore these hypotheses.

\(H_{0}\) : The mean weight of shipments that weren’t late is the same as the mean weight of shipments that were late.

\(H_{A}\) : The mean weight of shipments that weren’t late is less than the mean weight of shipments that were late.

You can run the test more concisely using infer’s t_test().

t_test() assumes that the null distribution is normal. We can avoid assumptions by using a simulation-based non-parametric equivalent.

# Fill out the null distribution pipeline
null_distn <- late_shipments %>%
  # Specify weight_kilograms vs. late 
  specify(weight_kilograms ~ late) %>%
  # Declare a null hypothesis of independence 
  hypothesize(null = "independence") %>%
  # Generate a 100 permutation replicates
  generate(reps = 1000, type = "permute") %>%
  # Calculate the difference in means ("No", "miuns", "Yes")
  calculate(stat = "diff in means", order = c("No", "Yes"))

# See the results
null_distn
Response: weight_kilograms (numeric)
Explanatory: late (factor)
Null Hypothesis: independence
# Calculate the observed difference in means 
obs_stat <- late_shipments %>%
  specify(weight_kilograms ~ late) %>%
  calculate(stat = "diff in means", order = c("No", "Yes"))

# See the results
obs_stat
Response: weight_kilograms (numeric)
Explanatory: late (factor)
# Get the p-value
p_value <- get_p_value(null_distn, obs_stat, direction = "less")

# See the result
p_value

Rank sum tests

Another class of non-parametric hypothesis tests are called rank sum tests. Ranks are the positions of numeric values from smallest to largest. Think of them as positions in running events: whoever has the fastest (smallest) time is rank 1, second fastest is rank 2, and so on.

By calculating on the ranks of data instead of the actual values, you can avoid making assumptions about the distribution of the test statistic. It’s most robust in the same way that a median is more robust than a mean.

Two commonly used rank-based tests are the Wilcoxon-Mann-Whitney test, which is like a non-parametric t-test, and the Kruskal-Wallis test, which is like a non-parametric ANOVA.

# Run a Wilcoxon-Mann-Whitney test on weight_kilograms ~ late
test_results <-   wilcox.test(weight_kilograms ~ late, data = late_shipments)

# See the results
test_results

    Wilcoxon rank sum test with continuity correction

data:  weight_kilograms by late
W = 21480, p-value = 1.861e-05
alternative hypothesis: true location shift is not equal to 0
# Run a Kruskal-Wallace test on weight_kilograms vs. shipment_mode 
test_results <- kruskal.test(weight_kilograms ~ shipment_mode, data = late_shipments)

# See the results
test_results

    Kruskal-Wallis rank sum test

data:  weight_kilograms by shipment_mode
Kruskal-Wallis chi-squared = 159.09, df = 2, p-value < 2.2e-16

END

LS0tDQp0aXRsZTogIkxhdGUgU2hpcG1lbnRzIEh5cG90aGVzaXMgVGVzdGluZyAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmF1dGhvcjogSnVhbiBGZXJuYW5kbyBNb3NxdWVyYSBBcmF1am8NCmRhdGU6IDIwMjQtMDMtMTcgDQotLS0NCg0KIyMjICoqQ2FsY3VsYXRpbmcgdGhlIHNhbXBsZSBtZWFuKioNCg0KVGhlIGBsYXRlX3NoaXBtZW50c2AgZGF0YXNldCBjb250YWlucyBzdXBwbHkgY2hhaW4gZGF0YSBvbiB0aGUgZGVsaXZlcnkgb2YgbWVkaWNhbCBzdXBwbGllcy4gRWFjaCByb3cgcmVwcmVzZW50cyBvbmUgZGVsaXZlcnkgb2YgYSBwYXJ0LiBUaGUgYGxhdGVgIGNvbHVtbnMgZGVub3RlcyB3aGV0aGVyIG9yIG5vdCB0aGUgcGFydCB3YXMgZGVsaXZlcmVkIGxhdGUuIEEgdmFsdWUgb2YgYCJZZXMiYCBtZWFucyB0aGF0IHRoZSBwYXJ0IHdhcyBkZWxpdmVyZWQgbGF0ZSwgYW5kIGEgdmFsdWUgb2YgYCJObyJgIG1lYW5zIHRoZSBwYXJ0IHdhcyBkZWxpdmVyZWQgb24gdGltZS4NCg0KTGV0J3MgYmVnaW4gb3VyIGFuYWx5c2lzIGJ5IGNhbGN1bGF0aW5nIGEgcG9pbnQgZXN0aW1hdGUgKHNhbXBsZSBzdGF0aXN0aWMpLCBuYW1lbHkgdGhlIHByb3BvcnRpb24gb2YgbGF0ZSBzaGlwbWVudHMuDQoNCmBgYHtyfQ0KIyBMb2FkIGxpYnJhcmllcw0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGZzdCkNCmxpYnJhcnkodGliYmxlKQ0KbGlicmFyeShyZWFkcikNCmBgYA0KDQpgYGB7cn0NCnBhdGhfbHRzaHAgPC0gIkM6L1VzZXJzL0p1YW5GZXIgTW9zcXVlcmEvRG9jdW1lbnRzL2RhdGFzZXRzL2xhdGVfc2hpcG1lbnRzLmZzdCINCmxhdGVfc2hpcG1lbnRzIDwtIHJlYWRfZnN0KHBhdGhfbHRzaHApDQoNCmdsaW1wc2UobGF0ZV9zaGlwbWVudHMpDQpsYXRlX3NoaXBtZW50cw0KYGBgDQoNCmBgYHtyfQ0KY29sbmFtZXMobGF0ZV9zaGlwbWVudHMpDQpgYGANCg0KYGBge3J9DQpzdW1tYXJ5KGxhdGVfc2hpcG1lbnRzKQ0KYGBgDQoNCiMjIyMgKipFeHBsb3JlIE4vQSB2YWx1ZXMqKg0KDQpgYGB7cn0NCmxhdGVfc2hpcG1lbnRzIDwtIGxhdGVfc2hpcG1lbnRzICU+JQ0KIG11dGF0ZShzaGlwbWVudF9tb2RlID0gbmFfaWYoc2hpcG1lbnRfbW9kZSwgIk4vQSIpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDb3VudCBOQSB2YWx1ZXMgaW4gZWFjaCBjb2x1bW4NCm5hX2NvdW50cyA8LSBjb2xTdW1zKGlzLm5hKGxhdGVfc2hpcG1lbnRzKSkNCg0KIyBTb3J0IHRoZSByZXN1bHRzIGluIGRlc2NlbmRpbmcgb3JkZXINCnNvcnRlZF9uYV9jb3VudHMgPC0gc29ydChuYV9jb3VudHMsIGRlY3JlYXNpbmcgPSBUUlVFKQ0KDQojIFNlZSB0aGUgcmVzdWx0DQpzb3J0ZWRfbmFfY291bnRzDQpgYGANCg0KYGBge3J9DQphcy5mYWN0b3IobGF0ZV9zaGlwbWVudHMkdmVuZG9yX2luY29fdGVybSkNCmBgYA0KDQojIyMgKipUZXN0aW5nIHRoZSBoeXBvdGhlc2VzKioNCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbiBvZiBsYXRlIHNoaXBtZW50cw0KbGF0ZV9wcm9wX3NhbXAgPC0gbGF0ZV9zaGlwbWVudHMgJT4lDQogIHN1bW1hcmlzZShwcm9wX2xhdGVfc2hpcG1lbnRzID0gbWVhbihsYXRlID09ICJZZXMiKSkgJT4lDQogIHB1bGwocHJvcF9sYXRlX3NoaXBtZW50cykNCg0KIyBzZWUgdGhlIHJlc3VsdHMNCmxhdGVfcHJvcF9zYW1wDQpgYGANCg0KIyMjICoqQ2FsY3VsYXRpbmcgYSB6LXNjb3JlKioNCg0KU2luY2UgdmFyaWFibGVzIGhhdmUgYXJiaXRyYXJ5IHJhbmdlcyBhbmQgdW5pdHMsIHdlIG5lZWQgdG8gc3RhbmRhcmRpemUgdGhlbS4gRm9yIGV4YW1wbGUsIGl0IHdvdWxkIGJlIHNpbGx5IGlmIGEgaHlwb3RoZXNpcyB0ZXN0IGdhdmUgYSBkaWZmZXJlbnQgYW5zd2VyIGlmIHlvdXIgdmFyaWFibGVzIHdlcmUgaW4gRXVyb3MgaW5zdGVhZCBvZiBVUyBkb2xsYXJzLiBTdGFuZGFyZGl6YXRpb24gYXZvaWRzIHRoYXQuDQoNCk9uZSBzdGFuZGFyZGl6ZWQgdmFsdWUgb2YgaW50ZXJlc3QgaW4gYSBoeXBvdGhlc2lzIHRlc3QgaXMgY2FsbGVkIGEgKip6LXNjb3JlKiouIFRvIGNhbGN1bGF0ZSBpdCwgd2UgbmVlZCB0aHJlZSBudW1iZXJzOiB0aGUgc2FtcGxlIHN0YXRpc3RpYyAocG9pbnQgZXN0aW1hdGUpLCB0aGUgaHlwb3RoZXNpemVkIHN0YXRpc3RpYywgYW5kIHRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgc3RhdGlzdGljICh3aGljaCB3ZSBlc3RpbWF0ZSBmcm9tIHRoZSBib290c3RyYXAgZGlzdHJpYnV0aW9uKS4NCg0KYGBge3J9DQpsYXRlX3NoaXBtZW50c19ib290X3NhbXBsZSA8LSByZXBsaWNhdGUoDQogIG4gPSA1MDAwLA0KICBleHByID0gew0KICAgIGxhdGVfc2hpcG1lbnRzICU+JQ0KICAgICAgc2xpY2Vfc2FtcGxlKHByb3AgPSAxLCByZXBsYWNlID0gVFJVRSkgJT4lDQogICAgICBzdW1tYXJpc2UocHJvcF9sYXRlX3NocG10ID0gbWVhbihsYXRlID09ICJZZXMiKSkgJT4lDQogICAgICBwdWxsKHByb3BfbGF0ZV9zaHBtdCkNCiAgfQ0KKQ0KaGVhZChsYXRlX3NoaXBtZW50c19ib290X3NhbXBsZSkNCmBgYA0KDQojIyMjICoqR2VuZXJhdGUgdGhlIGJvb3RzdHJhcCBkaXN0cmlidXRpb24qKg0KDQpgYGB7cn0NCmxhdGVfc2hpcG1lbnRzX2Jvb3RfZGlzdG4gPC0gdGliYmxlKGxhdGVfcHJvcCA9IGxhdGVfc2hpcG1lbnRzX2Jvb3Rfc2FtcGxlKSAlPiUNCiAgbXV0YXRlKHJlcGxpY2F0ZSA9IHJvd19udW1iZXIoKSkNCmxhdGVfc2hpcG1lbnRzX2Jvb3RfZGlzdG4NCm5yb3cobGF0ZV9zaGlwbWVudHNfYm9vdF9kaXN0bikNCmBgYA0KDQpgYGB7cn0NCiMgSHlwb3RoZXNpemUgdGhhdCB0aGUgcHJvcG9ydGlvbiBpcyA2JQ0KbGF0ZV9wcm9wX2h5cCA8LSAwLjA2DQoNCiMgQ2FsY3VsYXRlIHRoZSBzdGFuZGFyZCBlcnJvcg0Kc3RkX2Vycm9yIDwtIGxhdGVfc2hpcG1lbnRzX2Jvb3RfZGlzdG4gJT4lDQogIHN1bW1hcmlzZShzZCA9IHNkKGxhdGVfcHJvcCkpICU+JQ0KICBwdWxsKHNkKQ0KDQojIEZpbmQgei1zY29yZSBvZiBsYXRlX3Byb3Bfc2FtcA0Kel9zY29yZSA8LSAobGF0ZV9wcm9wX3NhbXAgLSBsYXRlX3Byb3BfaHlwKSAvIHN0ZF9lcnJvcg0KDQojIFNlZSB0aGUgcmVzdWx0cw0Kel9zY29yZQ0KYGBgDQoNCiMjIyAqKkNhbGN1bGF0aW5nIHAtdmFsdWVzKioNCg0KSW4gb3JkZXIgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdG8gY2hvb3NlIHRoZSBudWxsIGh5cG90aGVzaXMgb3IgdGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMsIHlvdSBuZWVkIHRvIGNhbGN1bGF0ZSBhIHAtdmFsdWUgZnJvbSB0aGUgei1zY29yZS4NCg0KTGV0J3MgcmV0dXJuIHRvIHRoZSBsYXRlIHNoaXBtZW50cyBkYXRhc2V0IGFuZCB0aGUgcHJvcG9ydGlvbiBvZiBsYXRlIHNoaXBtZW50cy4NCg0KVGhlIG51bGwgaHlwb3RoZXNpcywgSDAsIGlzIHRoYXQgdGhlIHByb3BvcnRpb24gb2YgbGF0ZSBzaGlwbWVudHMgaXMgNiUNCg0KVGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMsIEhhLCBpcyB0aGF0IHRoZSBwcm9wb3J0aW9uIG9mIGxhdGUgc2hpcG1lbnRzIGlzICoqZ3JlYXRlciB0aGFuKiogNiUNCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgei1zY29yZSBvZiBsYXRlX3Byb3Bfc2FtcA0Kel9zY29yZSA8LSAobGF0ZV9wcm9wX3NhbXAgLSBsYXRlX3Byb3BfaHlwKSAvIHN0ZF9lcnJvcg0KDQojIENhbGN1bGF0ZSB0aGUgcC12YWx1ZQ0KcF92YWx1ZSA8LSBwbm9ybSh6X3Njb3JlLCBsb3dlci50YWlsID0gRkFMU0UpDQoNCiMgU2VlIHRoZSByZXN1bHQgDQpwX3ZhbHVlDQpgYGANCg0KIyMjICoqQ2FsY3VsYXRpbmcgY29uZmlkZW5jZSBpbnRlcnZhbHMqKg0KDQpJZiB5b3UgZ2l2ZSBhIHNpbmdsZSBlc3RpbWF0ZSBvZiBhIHNhbXBsZSBzdGF0aXN0aWMsIHlvdSBhcmUgYm91bmQgdG8gYmUgd3JvbmcgYnkgc29tZSBhbW91bnQuIEZvciBleGFtcGxlLCB0aGUgaHlwb3RoZXNpemVkIHByb3BvcnRpb24gb2YgbGF0ZSBzaGlwbWVudHMgd2FzIDYlLiBFdmVuIGlmIGV2aWRlbmNlIHN1Z2dlc3RzIHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGUgcHJvcG9ydGlvbiBvZiBsYXRlIHNoaXBtZW50cyBpcyBlcXVhbCB0byB0aGlzLCBmb3IgYW55IG5ldyBzYW1wbGUgb2Ygc2hpcG1lbnRzLCB0aGUgcHJvcG9ydGlvbiBpcyBsaWtlbHkgdG8gYmUgYSBsaXR0bGUgZGlmZmVyZW50LiBDb25zZXF1ZW50bHksIGl0J3MgYSBnb29kIGlkZWEgdG8gc3RhdGUgYSBjb25maWRlbmNlIGludGVydmFsLiBUaGF0IGlzLCB5b3Ugc2F5ICJ3ZSBhcmUgOTUlICdjb25maWRlbnQnIHRoZSBwcm9wb3J0aW9uIG9mIGxhdGUgc2hpcG1lbnRzIGlzIGJldHdlZW4gQSBhbmQgQiIgKGZvciBzb21lIHZhbHVlIG9mIEEgYW5kIEIpLg0KDQpIZXJlLCB5b3UnbGwgdXNlIHF1YW50aWxlcyBvZiB0aGUgYm9vdHN0cmFwIGRpc3RyaWJ1dGlvbiB0byBjYWxjdWxhdGUgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwuDQoNCmBgYHtyfQ0KY29uZl9pbnRfcXVhbnRpbGUgPC0gbGF0ZV9zaGlwbWVudHNfYm9vdF9kaXN0biAlPiUNCiAgc3VtbWFyaXNlKGxvd2VyID0gcXVhbnRpbGUobGF0ZV9wcm9wLCAwLjAyNSksIA0KICAgICAgICAgICAgdXBwZXIgPSBxdWFudGlsZShsYXRlX3Byb3AsIDAuOTc1KSkNCg0KIyBTZWUgdGhlIHJlc3VsdA0KY29uZl9pbnRfcXVhbnRpbGUNCmBgYA0KDQojIyMgKipUd28gc2FtcGxlIG1lYW4gdGVzdCBzdGF0aXN0aWMqKg0KDQpUaGUgaHlwb3RoZXNpcyB0ZXN0IGZvciBkZXRlcm1pbmluZyBpZiB0aGVyZSBpcyBhIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbWVhbnMgb2YgdHdvIHBvcHVsYXRpb25zIHVzZXMgYSBkaWZmZXJlbnQgdHlwZSBvZiB0ZXN0IHN0YXRpc3RpYyB0byB0aGUgei1zY29yZXMuIEl0J3MgY2FsbGVkICJ0IiwgYW5kIGNhbiBiZSBjYWxjdWxhdGVkIGZyb20gdGhyZWUgdmFsdWVzIGZyb20gZWFjaCBzYW1wbGUgdXNpbmcgdGhpcyBlcXVhdGlvbi4kJA0KdCA9IFxmcmFjeyhcYmFye3h9X3tOb30gXCAtIFxiYXJ7eH1fe1llc30pfXtcc3FydHtcZnJhY3tzXjJfe05vfX17bl97Tm99fSBcICsgXGZyYWN7c14yX3tZZXN9fXtuX3tZZXN9fX19JCQNCg0KV2hpbGUgdHJ5aW5nIHRvIGRldGVybWluZSB3aHkgc29tZSBzaGlwbWVudHMgYXJlIGxhdGUsIHlvdSBtYXkgd29uZGVyIGlmIHRoZSB3ZWlnaHQgb2YgdGhlIHNoaXBtZW50cyB0aGF0IHdlcmUgbGF0ZSBpcyBkaWZmZXJlbnQgZnJvbSB0aGUgd2VpZ2h0IG9mIHRoZSBzaGlwbWVudHMgdGhhdCB3ZXJlIG9uIHRpbWUuIFRoZcKgYGxhdGVfc2hpcG1lbnRzYMKgZGF0YXNldCBoYXMgYmVlbiBzcGxpdCBpbnRvIGEgInllcyIgZ3JvdXAsIHdoZXJlwqBgbGF0ZSA9PSAiWWVzImDCoGFuZCBhICJubyIgZ3JvdXAgd2hlcmXCoGBsYXRlID09ICJObyJgLiBUaGUgd2VpZ2h0IG9mIHRoZSBzaGlwbWVudCBpcyBnaXZlbiBpbiB0aGXCoGB3ZWlnaHRfa2lsb2dyYW1zYMKgdmFyaWFibGUuDQoNCmBgYHtyfQ0KZ3JwX3Rlc3RzIDwtIGxhdGVfc2hpcG1lbnRzICU+JQ0KICBncm91cF9ieShsYXRlKSAlPiUNCiAgc3VtbWFyaXNlKHhfYmFyID0gbWVhbih3ZWlnaHRfa2lsb2dyYW1zKSwgcyA9IHNkKHdlaWdodF9raWxvZ3JhbXMpLCBuID0gbigpKQ0KDQojIFNlZSB0aGUgcmVzdWx0cw0KZ3JwX3Rlc3RzDQpgYGANCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgbnVtZXJhdG9yIG9mIHRoZSB0ZXN0IHN0YXRpc3RpYw0KbnVtZXJhdG9yIDwtIGdycF90ZXN0cyR4X2JhclsxXSAtIGdycF90ZXN0cyR4X2JhclsyXQ0KDQojIENhbGN1bGF0ZSB0aGUgZGVub21pbmF0b3Igb2YgdGhlIHRlc3Qgc3RhdGlzdGljDQpkZW5vbWluYXRvciA8LSBzcXJ0KChncnBfdGVzdHMkc1sxXV4yIC8gZ3JwX3Rlc3RzJG5bMV0pICsgKGdycF90ZXN0cyRzWzJdXjIgLyBncnBfdGVzdHMkblsyXSkpIA0KDQojIENhbGN1bGF0ZSB0aGUgdGVzdCBzdGF0aXN0aWNzIA0KdHN0YXQgPC0gbnVtZXJhdG9yIC8gZGVub21pbmF0b3INCg0KIyBTZWUgdGhlIHJlc3VsdA0KdHN0YXQNCmBgYA0KDQojIyMgKipGcm9tIHQgdG8gcCoqDQoNClByZXZpb3VzbHksIHlvdSBjYWxjdWxhdGVkIHRoZSB0ZXN0IHN0YXRpc3RpYyBmb3IgdGhlIHR3by1zYW1wbGUgcHJvYmxlbSBvZiB3aGV0aGVyIHRoZSBtZWFuIHdlaWdodCBvZiBzaGlwbWVudHMgaXMgbG93ZXIgZm9yIHNoaXBtZW50cyB0aGF0IHdlcmVuJ3QgbGF0ZSAoYGxhdGUgPT0gIk5vImApIGNvbXBhcmVkIHRvIHNoaXBtZW50cyB0aGF0IHdlcmUgbGF0ZSAoYGxhdGUgPT0gIlllcyJgKS4gSW4gb3JkZXIgdG8gbWFrZSBkZWNpc2lvbnMgYWJvdXQgaXQsIHlvdSBuZWVkIHRvIHRyYW5zZm9ybSB0aGUgdGVzdCBzdGF0aXN0aWMgd2l0aCBhIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIHRvIGdldCBhIHAtdmFsdWUuDQoNClJlY2FsbCB0aGUgaHlwb3RoZXNlczoNCg0KJEhfezB9JDogVGhlIG1lYW4gd2VpZ2h0IG9mIHNoaXBtZW50cyB0aGF0IHdlcmVuJ3QgbGF0ZSBpcyB0aGUgc2FtZSBhcyB0aGUgbWVhbiB3ZWlnaHQgb2Ygc2hpcG1lbnRzIHRoYXQgd2VyZSBsYXRlLg0KDQokSF97QX0kOiBUaGUgbWVhbiB3ZWlnaHQgb2Ygc2hpcG1lbnRzIHRoYXQgd2VyZW4ndCBsYXRlIGlzIGxlc3MgdGhhbiB0aGUgbWVhbiB3ZWlnaHQgb2Ygc2hpcG1lbnRzIHRoYXQgd2VyZSBsYXRlLg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBkZWdyZWVzIG9mIGZyZWVkb20NCmRlZ3JlZXNvZl9mcmVlZG9tIDwtIChncnBfdGVzdHMkblsxXSArIGdycF90ZXN0cyRuWzJdKSAtIDINCg0KIyBDYWxjdWxhdGUgdGhlIHAtdmFsdWUgZnJvLCB0aGUgdGVzdCBzdGF0IA0KcHZhbHVlIDwtIHB0KHRzdGF0LCBkZiA9IGRlZ3JlZXNvZl9mcmVlZG9tLCBsb3dlci50YWlsID0gRkFMU0UpDQoNCiMgU2VlIHRoZSByZXN1bHQNCnB2YWx1ZQ0KYGBgDQoNCiMjIyAqKlZpc3VhbGl6aW5nIG1hbnkgY2F0ZWdvcmllcyoqDQoNClNvIGZhciwgd2UndmUgb25seSAqKmNvbnNpZGVyZWQgdGhlIGNhc2Ugb2YgZGlmZmVyZW5jZXMgaW4gYSBudW1lcmljIHZhcmlhYmxlIGJldHdlZW4gdHdvIGNhdGVnb3JpZXMqKi4gT2YgY291cnNlLCBtYW55IGRhdGFzZXRzIGNvbnRhaW4gbW9yZSBjYXRlZ29yaWVzLiBCZWZvcmUgeW91IGdldCB0byBjb25kdWN0aW5nIHRlc3RzIG9uIG1hbnkgY2F0ZWdvcmllcywgaXQncyBvZnRlbiBoZWxwZnVsIHRvIHBlcmZvcm0gZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcy4gVGhhdCBpcywgY2FsY3VsYXRpbmcgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciBlYWNoIGdyb3VwIGFuZCB2aXN1YWxpemluZyB0aGUgZGlzdHJpYnV0aW9ucyBvZiB0aGUgbnVtZXJpYyB2YXJpYWJsZSBmb3IgZWFjaCBjYXRlZ29yeSB1c2luZyBib3ggcGxvdHMuDQoNCkhlcmUsIHdlJ2xsIHNlZSBob3cgdGhlIHByaWNlIG9mIGVhY2ggcGFja2FnZSAoYHBhY2tfcHJpY2VgKSB2YXJpZXMgYmV0d2VlbiB0aGUgdGhyZWUgc2hpcG1lbnQgbW9kZXMgKGBzaGlwbWVudF9tb2RlYCk6IGAiQWlyImAsIGAiQWlyIENoYXJ0ZXIiYCwgYW5kIGAiT2NlYW4iYC4NCg0KYGBge3J9DQojIFVzaW5nIGxhdGVfc2hpcG1lbnRzLCBncm91cCBieSBzaGlwbWVudF9tb2RlLCBhbmQgY2FsY3VsYXRlIHRoZSBtZWFuIGFuZCBzdGQgZGV2IG9mIHBhY2sgcHJpY2UNCmxhdGVfc2hpcG1lbnRzICU+JQ0KICBmaWx0ZXIoIWlzLm5hKHNoaXBtZW50X21vZGUpKSAlPiUNCiAgZ3JvdXBfYnkoc2hpcG1lbnRfbW9kZSkgJT4lDQogIHN1bW1hcmlzZSh4YmFyX3BhY2tfcHJpY2UgPSBtZWFuKHBhY2tfcHJpY2UsIG5hLnJtID0gVFJVRSksIHNfcGFja19wcmljZSA9IHNkKHBhY2tfcHJpY2UsIG5hLnJtID0gVFJVRSkpDQpgYGANCg0KYGBge3J9DQp1bmlxdWUobGF0ZV9zaGlwbWVudHMkc2hpcG1lbnRfbW9kZSkNCmBgYA0KDQpgYGB7cn0NCiMgVXNpbmcgbGF0ZSBzaGlwbWVudHMsIHBsb3QgcGFja19wcmljZSB2cy4gc2hpcG1lbnRfbW9kZSBhcyBhIGJveCBwbG90IHdpdGggZmxpcHBlZCB4IGFuZCB5IGNvb3JkaW5hdGVzDQpsYXRlX3NoaXBtZW50cyAlPiUNCiAgZmlsdGVyKCFpcy5uYShzaGlwbWVudF9tb2RlKSkgJT4lDQogIGdncGxvdChhZXMoc2hpcG1lbnRfbW9kZSwgcGFja19wcmljZSkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgY29vcmRfZmxpcCgpDQpgYGANCg0KIyMjICoqQ29uZHVjdGluZyBhbiBBTk9WQSB0ZXN0KioNCg0KVGhlIGJveCBwbG90cyBtYWRlIGl0IGxvb2sgbGlrZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHBhY2sgcHJpY2Ugd2FzIGRpZmZlcmVudCBmb3IgZWFjaCBvZiB0aGUgdGhyZWUgc2hpcG1lbnQgbW9kZXMuIEhvd2V2ZXIsICoqaXQgZGlkbid0IHRlbGwgdXMgd2hldGhlciB0aGUgbWVhbiBwYWNrIHByaWNlIHdhcyBkaWZmZXJlbnQgaW4gZWFjaCBjYXRlZ29yeSoqLiBUbyBkZXRlcm1pbmUgdGhhdCwgd2UgY2FuIHVzZSBhbiBBTk9WQSB0ZXN0LiBUaGUgbnVsbCBhbmQgYWx0ZXJuYXRpdmUgaHlwb3RoZXNlcyBjYW4gYmUgd3JpdHRlbiBhcyBmb2xsb3dzLg0KDQokSF97MH0kIDogUGFjayBwcmljZXMgZm9yIGV2ZXJ5IGNhdGVnb3J5IG9mIHNoaXBtZW50IG1vZGUgYXJlIHRoZSBzYW1lLg0KDQokSF97QX0kIDogUGFjayBwcmljZXMgZm9yIHNvbWUgY2F0ZWdvcmllcyBvZiBzaGlwbWVudCBtb2RlIGFyZSBkaWZmZXJlbnQuDQoNCioqV2UnbGwgc2V0IGEgc2lnbmlmaWNhbmNlIGxldmVsIG9mIDAuMSoqDQoNCmBgYHtyfQ0KIyBSdW4gYSBsaW5lYXIgcmVncmVzc2lvbiBvZiBwYWNrX3ByaWNlIHZzLiBzaGlwbWVudF9tb2RlDQptZGxfcGFja19waWNlX3ZzX3NoaXBtZW50X21vZGUgPC0gbG0ocGFja19wcmljZSB+IHNoaXBtZW50X21vZGUsIGRhdGEgPSBsYXRlX3NoaXBtZW50cykNCg0KIyBTZWUgdGhlIHJlc3VsdHMgDQpzdW1tYXJ5KG1kbF9wYWNrX3BpY2VfdnNfc2hpcG1lbnRfbW9kZSkNCmBgYA0KDQpgYGB7cn0NCiMgUGVyZm9ybSBBTk9WQSBvbiB0aGUgcmVncmVzc2lvbiBtb2RlbA0KYW5vdmEobWRsX3BhY2tfcGljZV92c19zaGlwbWVudF9tb2RlKQ0KYGBgDQoNCiMjIyAqKlBhaXJ3aXNlIHQtdGVzdHMqKg0KDQpUaGUgQU5PVkEgdGVzdCBkaWRuwrR0IHRlbGwgdXMgd2hpY2ggY2F0ZWdvcmllcyBvZiBzaGlwbWVudCBtb2RlIGhhZCBzaWduaWZpY2FudCBkaWZmZXJlbmNlcyBpbiBwYWNrX3ByaWNlcy4gVG8gcGlucG9pbnQgd2hpY2ggY2F0ZWdvcmllcyBoYWQgZGlmZmVyZW5jZXMsIHdlIGNvdWxkIGluc3RlYWQgdXNlIHBhaXJ3aXNlIHQtdGVzdHMuDQoNCmBgYHtyfQ0KIyBQZXJmb3JtIHBhaXJ3aXNlIHQtdGVzdHMgb24gcGFjayBwcmljZSwgZ3JvdXBlZCBieSBzaGlwbWVudF9tb2RlLCBubyBwLXZhbHVlIGFkanVzdG1lbnQuDQp0ZXN0X3Jlc3VsdHMgPC0gcGFpcndpc2UudC50ZXN0KGxhdGVfc2hpcG1lbnRzJHBhY2tfcHJpY2UsIGxhdGVfc2hpcG1lbnRzJHNoaXBtZW50X21vZGUsIHAuYWRqdXN0Lm1ldGhvZCA9ICJub25lIikNCg0KIyBTZWUgdGhlIHJlc3VsdHMNCnRlc3RfcmVzdWx0cw0KYGBgDQoNCmBgYHtyfQ0KIyBNb2RpZnkgdGhlIHBhaXJ3aXNlIHQtdGVzdHMgdG8gdXNlIEJvbmZlcnJvbmkgcC12YWx1ZSBhZGp1c3RtZW50DQp0ZXN0X3Jlc3VsdHMgPC0gcGFpcndpc2UudC50ZXN0KGxhdGVfc2hpcG1lbnRzJHBhY2tfcHJpY2UsIGxhdGVfc2hpcG1lbnRzJHNoaXBtZW50X21vZGUsIHAuYWRqdXN0Lm1ldGhvZCA9ICJib25mZXJyb25pIikNCg0KIyBTZWUgdGhlIHJlc3VsdHMgDQp0ZXN0X3Jlc3VsdHMNCmBgYA0KDQojIyMgKipUZXN0IG9mIHRobyBwcm9wb3J0aW9ucyoqDQoNCllvdSBtYXkgd29uZGVyIGlmIHRoZSBhbW91bnQgcGFpZCBmb3IgZnJlaWdodCBhZmZlY3RzIHdoZXRoZXIgb3Igbm90IHRoZSBzaGlwbWVudCB3YXMgbGF0ZS4gUmVjYWxsIHRoYXQgaW7CoGBsYXRlX3NoaXBtZW50c2DCoGRhdGFzZXQsIHdoZXRoZXIgb3Igbm90IHRoZSBzaGlwbWVudCB3YXMgbGF0ZSBpcyBzdG9yZWQgaW4gdGhlwqBgbGF0ZWDCoGNvbHVtbi4gRnJlaWdodCBjb3N0cyBhcmUgc3RvcmVkIGluIHRoZcKgYGZyZWlnaHRfY29zdF9ncm91cGDCoGNvbHVtbiwgYW5kIHRoZSBjYXRlZ29yaWVzIGFyZcKgYCJleHBlbnNpdmUiYMKgYW5kwqBgInJlYXNvbmFibGUiYC4NCg0KV2UgY2FuIGZvcm0gdGhlIGh5cG90aGVzZXMgdG8gdGVzdDoNCg0KJCQNCkhfezB9OiBsYXRlX3tleHBlbnNpdmV9IFwgLSBsYXRlX3tyZWFzb25hYmxlfSBcID0gMCBcXA0KSF97QX06IGxhdGVfe2V4cGVuc2l2ZX0gXCAtIGxhdGVfe3JlYXNvbmFibGV9IFwgPiAwDQokJA0KDQpgcF9oYXRzYMKgY29udGFpbnMgdGhlIGVzdGltYXRlcyBvZiBwb3B1bGF0aW9uIHByb3BvcnRpb25zIChzYW1wbGUgcHJvcG9ydGlvbnMpIGZvciB0aGUgImV4cGVuc2l2ZSIgYW5kICJyZWFzb25hYmxlIiBncm91cHMuwqBgbnNgwqBjb250YWlucyB0aGUgc2FtcGxlIHNpemVzIGZvciB0aGVzZSBncm91cHMuDQoNCiMjIyBDYWxjdWxhdGluZyBwcm9wb3J0aW9ucyAmIHNhbXBsZSBzaXplcw0KDQokbGF0ZV97ZXhwZW5zaXZlfSBcIGFuZCBcIGxhdGVfe3JlYXNvbmFibGV9JA0KDQpgYGB7cn0NCmxhdGVfc2hpcG1lbnRzIDwtIGxhdGVfc2hpcG1lbnRzICU+JQ0KICBtdXRhdGUoZnJlaWdodF9jb3N0X2dyb3VwID0gaWZlbHNlKGZyZWlnaHRfY29zdF91c2QgPiA1MDAwLCAiZXhwZW5zaXZlIiwgInJlYXNvbmFibGUiKSkNCmBgYA0KDQpgYGB7cn0NCnByb3BfYWJzX3llcyA8LSBsYXRlX3NoaXBtZW50cyAlPiUNCiAgY291bnQobGF0ZSwgZnJlaWdodF9jb3N0X2dyb3VwKSAlPiUNCiAgbXV0YXRlKGFic19wcm9wID0gbiAvIG5yb3cobGF0ZV9zaGlwbWVudHMpKSAlPiUNCiAgZmlsdGVyKGxhdGUgPT0gIlllcyIgJiAoZnJlaWdodF9jb3N0X2dyb3VwID09ICJyZWFzb25hYmxlIiB8IGZyZWlnaHRfY29zdF9ncm91cCA9PSAiZXhwZW5zaXZlIikpICU+JQ0KICBzZWxlY3QoYWJzX3Byb3ApDQoNCnByb3BfYWJzX3llcw0KYGBgDQoNCmBgYHtyfQ0KcF9oYXRzIDwtIGFzLnZlY3Rvcihwcm9wX2Fic195ZXMkYWJzX3Byb3ApDQpuYW1lcyhwX2hhdHMpIDwtIGMoImV4cGVuc2l2ZSIsICJyZWFzb25hYmxlIikNCnBfaGF0cw0KYGBgDQoNCmBgYHtyfQ0Kc21wbF9zaXplIDwtIGxhdGVfc2hpcG1lbnRzICU+JQ0KICBjb3VudChmcmVpZ2h0X2Nvc3RfZ3JvdXApICU+JQ0KICBtdXRhdGUoY291bnQgPSBuKSAlPiUNCiAgI2ZpbHRlcihmcmVpZ2h0X2Nvc3RfZ3JvdXAgPT0gImV4cGVuc2l2ZSIpICU+JQ0KICBzZWxlY3QoY291bnQpDQoNCnNtcGxfc2l6ZQ0KYGBgDQoNCmBgYHtyfQ0KbnMgPC0gYXMudmVjdG9yKHNtcGxfc2l6ZSRjb3VudCkNCm5hbWVzKG5zKSA8LSBjKCJleHBlbnNpdmUiLCAicmVhc29uYWJsZSIpDQpucw0KYGBgDQoNCiMjIyAqKlRlc3Qgb2YgdHdvIHByb3BvcnRpb25zKioNCg0KV2UgbWF5IHdvbmRlciBpZiB0aGUgYW1vdW50IHBhaWQgZm9yIGZyZWlnaHQgYWZmZWN0cyB3aGV0aGVyIG9yIG5vdCB0aGUgc2hpcG1lbnQgd2FzIGxhdGUuIFJlY2FsbCB0aGF0IGluwqBgbGF0ZV9zaGlwbWVudHNgwqBkYXRhc2V0LCB3aGV0aGVyIG9yIG5vdCB0aGUgc2hpcG1lbnQgd2FzIGxhdGUgaXMgc3RvcmVkIGluIHRoZcKgYGxhdGVgwqBjb2x1bW4uIEZyZWlnaHQgY29zdHMgYXJlIHN0b3JlZCBpbiB0aGXCoGBmcmVpZ2h0X2Nvc3RfZ3JvdXBgwqBjb2x1bW4sIGFuZCB0aGUgY2F0ZWdvcmllcyBhcmXCoGAiZXhwZW5zaXZlImDCoGFuZMKgYCJyZWFzb25hYmxlImAuDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIHBvb2xlZCBlc3RpbWF0ZSBvZiB0aGUgcG9wdWxhdGlvbiBwcm9wb3J0aW9uDQpwX2hhdCA8LSB3ZWlnaHRlZC5tZWFuKHBfaGF0cywgbnMpDQoNCiMgU2VlIHRoZSByZXN1bHQNCnBfaGF0DQpgYGANCg0KYGBge3J9DQojIENhbGN1bGF0ZSBzYW1wbGUgcHJvcMK0biB0aW1lcyBvbmUgbWludXMgc2FtcGxlIHByb3DCtG4NCnBfaGF0X3RpbWVzX25vdF9wX2hhdCA8LSBwX2hhdCAqICgxIC0gcF9oYXQpDQoNCiMgRGl2aWRlIHRoaXMgYnkgdGhlIHNhbXBsZSBzaXplcyANCnBfaGF0X3RpbWVzX25vdF9wX2hhdF9vdmVyX25zIDwtIHBfaGF0X3RpbWVzX25vdF9wX2hhdCAvIG5zDQoNCiMgQ2FsY3VsYXRlIHN0ZC4gZXJyb3INCnN0ZF9lcnJvciA8LSBzcXJ0KHN1bShwX2hhdF90aW1lc19ub3RfcF9oYXRfb3Zlcl9ucykpDQoNCiMgU2VlIHRoZSByZXN1bHQgDQpzdGRfZXJyb3INCmBgYA0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSB6LXNjb3JlIA0Kel9zY29yZSA8LSAocF9oYXRzWyJleHBlbnNpdmUiXSAtIHBfaGF0c1sicmVhc29uYWJsZSJdKSAvIHN0ZF9lcnJvcg0KDQojIFNlZSB0aGUgcmVzdWx0DQp6X3Njb3JlDQpgYGANCg0KIyMjICoqcHJvcF90ZXN0KCkgZm9yIHR3byBzYW1wbGVzKioNCg0KVGhhdCB0b29rIGEgbG90IG9mIGVmZm9ydCB0byBjYWxjdWxhdGUgdGhlIHAtdmFsdWUsIHNvIHdoaWxlIGl0IGlzIHVzZWZ1bCB0byBzZWUgaG93IHRoZSBjYWxjdWxhdGlvbnMgd29yaywgaXQgaXNuJ3QgcHJhY3RpY2FsIHRvIGRvIGluIHJlYWwtd29ybGQgYW5hbHlzZXMuIEZvciBkYWlseSB1c2FnZSwgaXQncyBiZXR0ZXIgdG8gdXNlIHRoZcKgYGluZmVyYMKgcGFja2FnZS4NCg0KUmVjYWxsIHRoZSBoeXBvdGhlc2VzLiQkDQpIX3swfTogbGF0ZV97ZXhwZW5zaXZlfSBcIC0gbGF0ZV97cmVhc29uYWJsZX0gXCA9IDAgXFwNCkhfe0F9OiBsYXRlX3tleHBlbnNpdmV9IFwgLSBsYXRlX3tyZWFzb25hYmxlfSBcID4gMA0KJCQNCg0KVXNpbmcgdGhlwqBgbGF0ZV9zaGlwbWVudHNgwqBkYXRhc2V0LCB1c2XCoGBwcm9wX3Rlc3QoKWDCoHRvIHBlcmZvcm0gYSBwcm9wb3J0aW9uIHRlc3QgYXBwcm9wcmlhdGUgdG8gdGhlIGh5cG90aGVzZXMuDQoNCmBgYHtyfQ0KbGlicmFyeShpbmZlcikNCiMgUGVyZm9ybSBhIHByb3BvcnRpb24gdGVzdCBhcHByb3BpYXRlIHRvIHRoZSBoeXBvdGhlc2lzDQoNCnRlc3RfcmVzdWx0cyA8LSBsYXRlX3NoaXBtZW50cyAlPiUNCiAgcHJvcF90ZXN0KGxhdGUgfiBmcmVpZ2h0X2Nvc3RfZ3JvdXAsIG9yZGVyID0gYygiZXhwZW5zaXZlIiwgInJlYXNvbmFibGUiKSwgc3VjY2VzcyA9ICJZZXMiLA0KICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAiZ3JlYXRlciIsIGNvcnJlY3QgPSBGQUxTRSkNCg0KIyBTZWUgdGhlIHJlc3VsdHMgDQp0ZXN0X3Jlc3VsdHMNCmBgYA0KDQojIyMgKipQZXJmb3JtaW5nIGEgY2hpLXNxdWFyZSB0ZXN0KioNCg0KVGhlICpjaGktc3F1YXJlIGluZGVwZW5kZW5jZSB0ZXN0KiBjb21wYXJlcyBwcm9wb3J0aW9ucyBvZiBzdWNjZXNzZXMgb2YgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBhY3Jvc3MgY2F0ZWdvcmllcyBvZiBhbm90aGVyIGNhdGVnb3JpY2FsIHZhcmlhYmxlLg0KDQpUcmFkZSBkZWFscyBvZnRlbiB1c2UgYSBmb3JtIG9mIGJ1c2luZXNzIHNob3J0aGFuZCBpbiBvcmRlciB0byBzcGVjaWZ5IHRoZSBleGFjdCBkZXRhaWxzIG9mIHRoZWlyIGNvbnRyYWN0LiBUaGVzZSBhcmUgSW50ZXJuYXRpb25hbCBDaGFtYmVyIG9mIENvbW1lcmNlIChJQ0MpIGludGVybmF0aW9uYWwgY29tbWVyY2lhbCB0ZXJtcywgb3IgKmluY290ZXJtcyogZm9yIHNob3J0Lg0KDQpUaGUgYGxhdGVfc2hpcG1lbnRzYCBkYXRhc2V0IGluY2x1ZGVzIGEgYHZlbmRvcl9pbmNvX3Rlcm1gIHRoYXQgZGVzY3JpYmVzIHRoZSBpbmNvdGVybXMgdGhhdCBhcHBsaWVkIHRvIGEgZ2l2ZW4gc2hpcG1lbnQuIFRoZSBjaG9pY2VzIGFyZToNCg0KLSAgIFsqKmBFWFdgKipdKGh0dHBzOi8vd3d3LmludmVzdG9wZWRpYS5jb20vdGVybXMvZS9leHcuYXNwKTogIkV4IHdvcmtzIi4gVGhlIGJ1eWVyIHBheXMgZm9yIHRyYW5zcG9ydGF0aW9uIG9mIHRoZSBnb29kcy4NCg0KLSAgIFsqKmBDSVBgKipdKGh0dHBzOi8vd3d3LmludmVzdG9wZWRpYS5jb20vdGVybXMvYy9jYXJyaWFnZS1hbmQtaW5zdXJhbmNlLXBhaWQtY2lwLmFzcCk6ICJDYXJyaWFnZSBhbmQgaW5zdXJhbmNlIHBhaWQgdG8iLiBUaGUgc2VsbGVyIHBheXMgZm9yIGZyZWlnaHQgYW5kIGluc3VyYW5jZSB1bnRpbCB0aGUgZ29vZHMgYm9hcmQgYSBzaGlwLg0KDQotICAgWyoqYEREUGAqKl0oaHR0cHM6Ly93d3cuaW52ZXN0b3BlZGlhLmNvbS90ZXJtcy9kL2RlbGl2ZXJ5LWR1dHktcGFpZC5hc3ApOiAiRGVsaXZlcmVkIGR1dHkgcGFpZCIuIFRoZSBzZWxsZXIgcGF5cyBmb3IgdHJhbnNwb3J0YXRpb24gb2YgdGhlIGdvb2RzIHVudGlsIHRoZXkgcmVhY2ggYSBkZXN0aW5hdGlvbiBwb3J0Lg0KDQotICAgWyoqYEZDQWAqKl0oaHR0cHM6Ly93d3cuaW52ZXN0b3BlZGlhLmNvbS90ZXJtcy9mL2ZjYS5hc3ApOiAiRnJlZSBjYXJyaWVyIi4gVGhlIHNlbGxlciBwYXlzIGZvciB0cmFuc3BvcnRhdGlvbiBvZiB0aGUgZ29vZHMuDQoNClBlcmhhcHMgdGhlICoqaW5jb3Rlcm1zKiogYWZmZWN0IHdoZXRoZXIgb3Igbm90IHRoZSBmcmVpZ2h0IGNvc3RzIGFyZSBleHBlbnNpdmUuIFRlc3QgdGhlc2UgaHlwb3RoZXNlcyB3aXRoIGEgc2lnbmlmaWNhbmNlIGxldmVsIG9mwqBgMC4wMWAuDQoNCiRIX3swfSQgOiBgdmVuZG9yX2luY29fdGVybWAgYW5kIGBmcmVpZ2h0X2Nvc3RfZ3JvdXBgIGFyZSBpbmRlcGVuZGVudA0KDQokSF97QX0kIDogYHZlbmRvcl9pbmNvX3Rlcm1gIGFuZCBgZnJlaWdodF9jb3N0X2dyb3VwYCBhcmUgYXNzb2NpYXRlZA0KDQpgYGB7cn0NCiMgUGxvdCB2ZW5kb3JfaW5jb190ZXJtIGZpbGxlZCBieSBmcmVpZ2h0X2Nvc3RfZ3JvdXAgDQojIE1ha2UgaXQgYSBwcm9wb3J0aW9uYWwgc3RhY2tlZCBiYXIgcGxvdA0KZ2dwbG90KGxhdGVfc2hpcG1lbnRzLCBhZXModmVuZG9yX2luY29fdGVybSwgZmlsbCA9IGZyZWlnaHRfY29zdF9ncm91cCkpICsgDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArIA0KICB5bGFiKCJwcm9wb3J0aW9uIikNCmBgYA0KDQpgYGB7cn0NCiMgUGVyZm9ybSBhIGNoaS1zcXVhcmUgdGVzdCBvZiBpbmRlcGVuZGVuY2Ugb24gZnJlaWdodF9jb3N0X2dyb3VwIGFuZCB2ZW5kb3JfaW5jb190ZXJtIA0KdGVzdF9yZXN1bHRzX3NvIDwtIGxhdGVfc2hpcG1lbnRzICU+JQ0KICBjaGlzcV90ZXN0KHZlbmRvcl9pbmNvX3Rlcm0gfiBmcmVpZ2h0X2Nvc3RfZ3JvdXApDQoNCiMgU2VlIHJlc3VsdHMNCnRlc3RfcmVzdWx0c19zbw0KYGBgDQoNCiMjIyMgKipSZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyoqDQoNCiMjIyAqKlZpc3VhbGl6aW5nIGdvb2RuZXNzIG9mIGZpdCoqDQoNClRoZSAqY2hpLXNxdWFyZSBnb29kbmVzcyBvZiBmaXQgdGVzdCogY29tcGFyZXMgcHJvcG9ydGlvbnMgb2YgZWFjaCBsZXZlbCBvZiBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIHRvIGh5cG90aGVzaXplZCB2YWx1ZXMuIEJlZm9yZSBydW5uaW5nIHN1Y2ggYSB0ZXN0LCBpdCBjYW4gYmUgaGVscGZ1bCB0byB2aXN1YWxseSBjb21wYXJlIHRoZSBkaXN0cmlidXRpb24gaW4gdGhlIHNhbXBsZSB0byB0aGUgaHlwb3RoZXNpemVkIGRpc3RyaWJ1dGlvbi4NCg0KUmVjYWxsIHRoZSB2ZW5kb3IgaW5jb3Rlcm1zIGluIHRoZSBgbGF0ZV9zaGlwbWVudHNgIGRhdGFzZXQuIExldCdzIGh5cG90aGVzaXplIHRoYXQgdGhlIGZvdXIgdmFsdWVzIG9jY3VyIHdpdGggdGhlc2UgZnJlcXVlbmNpZXMgaW4gdGhlIHBvcHVsYXRpb24gb2Ygc2hpcG1lbnRzLg0KDQotICAgYEVYV2A6IDAuNw0KDQotICAgYENJUGA6IDAuMDUNCg0KLSAgIGBDSUZgOiAwLjA1DQoNCi0gICBgREFQYDogMC4wNQ0KDQotICAgYEREVWA6IDAuMDUNCg0KLSAgIGBERFBgOiAwLjA1DQoNCi0gICBgRkNBYDogMC4wNQ0KDQpgYGB7cn0NCiMgVXNpbmcgbGF0ZV9zaGlwbWVudHMsbSBjb3VudCB0aGUgdmVuZG9yIGluY290ZXJtcw0KdmVuZG9yX2luY29fdGVybV9jb3VudHMgPC0gbGF0ZV9zaGlwbWVudHMgJT4lDQogIGNvdW50KHZlbmRvcl9pbmNvX3Rlcm0pDQoNCnZlbmRvcl9pbmNvX3Rlcm1fY291bnRzDQpgYGANCg0KYGBge3J9DQojIEdldCB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIHdob2xlIHNhbXBsZQ0Kbl90b3RhbCA8LSBucm93KGxhdGVfc2hpcG1lbnRzKQ0Kbl90b3RhbA0KYGBgDQoNCmBgYHtyfQ0KaHlwb3RoZXNpemVkX2luY290IDwtIHRyaWJibGUofiB2ZW5kb3JfaW5jb190ZXJtLCB+IHByb3AsICJFWFciLCAwLjc1LCAiQ0lQIiwgMC4wNSwgIkNJRiIsIDAuMCwgIkRBUCIsIDAuMCwgIkREVSIsIDAuMCwgIkREUCIsIDAuMSwgIkZDQSIsIDAuMSkgJT4lDQogICMgQWRkIGEgY29sdW1uIG9mIGh5cG90aGVzaXplZCBjb3VudHMgZm9yIHRoZSBpbmNvdGVybXMNCiAgbXV0YXRlKG4gPSBwcm9wICogbl90b3RhbCkNCg0KaHlwb3RoZXNpemVkX2luY290DQpgYGANCg0KYGBge3J9DQojIFVzaW5nIHZlbmRvcl9pbmNvX3Rlcm1fY291bnRzLCBwbG90IG4gdnMuIHZlbmRvcl9pbmNvX3Rlcm0gDQpnZ3Bsb3QodmVuZG9yX2luY29fdGVybV9jb3VudHMsIGFlcyh2ZW5kb3JfaW5jb190ZXJtLCBuKSkgKw0KICAjIE1ha2UgaXQgYSAocHJlY2FsY3VsYXRlZCkgYmFyIHBsb3QNCiAgZ2VvbV9jb2woKSArDQogICMgQWRkIHBvaW50cyBmcm9tIGh5cG90aGVzaXplZCANCiAgZ2VvbV9wb2ludChkYXRhPWh5cG90aGVzaXplZF9pbmNvdCwgY29sb3IgPSAiZ3JlZW4iKQ0KYGBgDQoNCiMjIyAqKlBlcmZvcm1pbmcgYSBnb29kbmVzcyBvZiBmaXQgdGVzdCoqDQoNClRoZSBiYXIgcGxvdCBvZiBgdmVuZG9yX2luY29fdGVybWAgc3VnZ2VzdGVkIHRoYXQgaXRzIGRpc3RyaWJ1dGlvbiBhY3Jvc3MgdGhlIGZvdXIgY2F0ZWdvcmllcyB3YXMgcXVpdGUgY2xvc2UgdG8gdGhlIGh5cG90aGVzaXplZCBkaXN0cmlidXRpb24uIFlvdSdsbCBuZWVkIHRvIHBlcmZvcm0gYSAqY2hpLXNxdWFyZSBnb29kbmVzcyBvZiBmaXQgdGVzdCogdG8gc2VlIHdoZXRoZXIgdGhlIGRpZmZlcmVuY2VzIGFyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50Lg0KDQpUbyBkZWNpZGUgd2hpY2ggaHlwb3RoZXNpcyB0byBjaG9vc2UsIHdlJ2xsIHNldCBhIHNpZ25pZmljYW5jZSBsZXZlbCBvZiBgMC4xYC4NCg0KYGBge3J9DQpoeXBvdGhlc2l6ZWRfcHJvcHMgPC0gYygiRVhXIiA9IDAuNzUsICJDSVAiID0gMC4wNSwgIkNJRiI9IDAuMCwgIkRBUCIgPSAwLjAsICJERFUiID0gMC4wLCAiRERQIj0gMC4xLCAiRkNBIiA9IDAuMSkNCg0KaHlwb3RoZXNpemVkX3Byb3BzDQpgYGANCg0KYGBge3J9DQojIFJ1biBjaGktc3F1YXJlIGdvb2RuZXNzIG9mIGZpdCB0ZXN0IG9uIHZlbmRvcl9pbmNvX3Rlcm0NCnRlc3RfcmVzdWx0cyA8LSBsYXRlX3NoaXBtZW50cyAlPiUNCiAgY2hpc3FfdGVzdChyZXNwb25zZSA9IHZlbmRvcl9pbmNvX3Rlcm0sIHAgPSBoeXBvdGhlc2l6ZWRfcHJvcHMpDQoNCnRlc3RfcmVzdWx0cw0KYGBgDQoNCiMjICoqTm9uLVBhcmFtZXRyaWMgdGVzdHMqKg0KDQojIyMgKipBc3N1bXB0aW9ucyBpbiBoeXBvdGhlc2lzIHRlc3RpbmcqKg0KDQpFYWNoIGh5cG90aGVzaXMgdGVzdCBtYWtlcyBhc3N1bXB0aW9ucyBhYm91dCB0aGUgZGF0YS4gSXQncyBvbmx5IHdoZW4gdGhlc2UgYXNzdW1wdGlvbnMgYXJlIG1ldCB0aGF0IGl0IGlzIGFwcHJvcHJpYXRlIHRvIHVzZSB0aGF0IGh5cG90aGVzaXMgdGVzdC4NCg0KIyMjICoqUmFuZG9tbmVzcyoqDQoNCldoZXRoZXIgaXQgdXNlcyBvbmUgb3IgdHdvIHNhbXBsZXMsICoqZXZlcnkgaHlwb3RoZXNpcyB0ZXN0IGFzc3VtZXMgdGhhdCBlYWNoIHNhbXBsZSBpcyByYW5kb21seSBzb3VyY2VkIGZyb20gaXRzIHBvcHVsYXRpb24qKi4gSWYgeW91IGRvbid0IGhhdmUgYSByYW5kb20gc2FtcGxlLCB0aGVuIGl0IHdvbid0IGJlIHJlcHJlc2VudGF0aXZlIG9mIHRoZSBwb3B1bGF0aW9uLiBUbyBjaGVjayB0aGlzIGFzc3VtcHRpb24sIHlvdSBuZWVkIHRvIGtub3cgd2hlcmUgeW91ciBkYXRhIGNhbWUgZnJvbS4gVGhlcmUgYXJlIG5vIHN0YXRpc3RpY2FsIG9yIGNvZGluZyB0ZXN0cyB5b3UgY2FuIHBlcmZvcm0gdG8gY2hlY2sgdGhpcy4gSSoqZiBpbiBkb3VidCwgYXNrIHRoZSBwZW9wbGUgaW52b2x2ZWQgaW4gY29sbGVjdGluZyB0aGUgZGF0YSwgb3IgYSBkb21haW4gZXhwZXJ0IHRoYXQgdW5kZXJzdGFuZHMgdGhlIHBvcHVsYXRpb24gYmVpbmcgc2FtcGxlZCoqLg0KDQojIyMgKipJbmRlcGVuZGVuY2Ugb2Ygb2JzZXJ2YXRpb25zKioNCg0KVGVzdHMgYWxzbyBhc3N1bWUgdGhhdCBlYWNoIG9ic2VydmF0aW9uIGlzIGluZGVwZW5kZW50LiBUaGVyZSBhcmUgc29tZSBzcGVjaWFsIGNhc2VzIGxpa2UgKipwYWlyZWQgdC10ZXN0cyoqIHdoZXJlIGRlcGVuZGVuY2llcyBiZXR3ZWVuIHR3byBzYW1wbGVzIGFyZSBhbGxvd2VkLCBidXQgdGhlc2UgY2hhbmdlIHRoZSBjYWxjdWxhdGlvbnMgc28geW91IG5lZWQgdG8gdW5kZXJzdGFuZCB3aGVyZSBzdWNoIGRlcGVuZGVuY2llcyBvY2N1ci4gQXMgeW91IHNhdyB3aXRoIHRoZSBwYWlyZWQgdC10ZXN0LCBub3QgYWNjb3VudGluZyBmb3IgZGVwZW5kZW5jaWVzIHJlc3VsdHMgaW4gYW4gaW5jcmVhc2VkIGNoYW5jZSBvZiBmYWxzZSBuZWdhdGl2ZSBhbmQgZmFsc2UgcG9zaXRpdmUgZXJyb3JzLiBUaGlzIGlzIGFsc28gYSBkaWZmaWN1bHQgcHJvYmxlbSB0byBkaWFnbm9zZSBhZnRlciB5b3UgaGF2ZSB0aGUgZGF0YS4gSXQgbmVlZHMgdG8gYmUgZGlzY3Vzc2VkIGJlZm9yZSBkYXRhIGNvbGxlY3Rpb24uDQoNCiMjIyAqKkxhcmdlIHNhbXBsZSBzaXplKioNCg0KSHlwb3RoZXNpcyB0ZXN0cyBhbHNvIGFzc3VtZSB0aGF0IHlvdXIgc2FtcGxlIGlzIGJpZyBlbm91Z2guICoqU21hbGxlciBzYW1wbGVzIGluY3VyIGdyZWF0ZXIgdW5jZXJ0YWludHkqKiwgKiphbmQgbWVhbiB0aGF0IHRoZSBDZW50cmFsIExpbWl0IFRoZW9yZW0gZG9lc24ndCBhcHBseSoqLCB3aGljaCBpbiB0dXJuIG1lYW5zIHRoYXQgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBtaWdodCBub3QgYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuICoqVGhlIGluY3JlYXNlZCB1bmNlcnRhaW50eSBtZWFucyB5b3UgZ2V0IHdpZGVyIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIG9uIHRoZSBwYXJhbWV0ZXIgeW91IGFyZSB0cnlpbmcgdG8gZXN0aW1hdGUqKi4gVGhlIENlbnRyYWwgTGltaXQgVGhlb3JlbSBub3QgYXBwbHlpbmcgbWVhbnMgdGhlIGNhbGN1bGF0aW9ucyBvbiB0aGUgc2FtcGxlIGNvdWxkIGJlIG5vbnNlbnNlLCB3aGljaCAqKmluY3JlYXNlcyB0aGUgY2hhbmNlIG9mIGZhbHNlIG5lZ2F0aXZlIGFuZCBwb3NpdGl2ZSBlcnJvcnMqKi4gVGhlIGNoZWNrIGZvciAiYmlnIGVub3VnaCIgZGVwZW5kcyBvbiB0aGUgdGVzdCBhbmQgdGhhdCdzIHdoZXJlIHdlJ2xsIGhlYWQgbmV4dC4NCg0KIyMjICoqTGFyZ2Ugc2FtcGxlIHNpemU6IHQtdGVzdCoqDQoNCkZvciBvbmUgc2FtcGxlIHQtdGVzdHMsIGEgcG9wdWxhciBoZXVyaXN0aWMgaXMgdGhhdCB5b3UgbmVlZCBhdCBsZWFzdCB0aGlydHkgb2JzZXJ2YXRpb25zIGluIHlvdXIgc2FtcGxlLiBGb3IgdGhlIHR3byBzYW1wbGUgY2FzZSBvciBBTk9WQSwgeW91IG5lZWQgdGhpcnR5IG9ic2VydmF0aW9ucyBpbiBlYWNoLiBUaGF0IG1lYW5zIHlvdSBjYW4ndCBjb21wZW5zYXRlIGZvciBvbmUgc21hbGwgZ3JvdXAgc2FtcGxlIGJ5IG1ha2luZyB0aGUgb3RoZXIgb25lIGJpZ2dlci4gSW4gdGhlIHBhaXJlZCBjYXNlLCB5b3UgbmVlZCB0aGlydHkgcGFpcnMgb2Ygb2JzZXJ2YXRpb25zLg0KDQpeMV4gU29tZXRpbWVzIHlvdSBjYW4gZ2V0IGF3YXkgd2l0aCBsZXNzIHRoYW4gMzA7IHRoZSBpbXBvcnRhbnQgdGhpbmcgaXMgdGhhdCB0aGUgbnVsbCBkaXN0cmlidXRpb24gYXBwZWFycyBub3JtYWwuDQoNCiMjIyMgKipPbmUgc2FtcGxlKioNCg0KLSAgIEF0IGxlYXN0IDMwIG9ic2VydmF0aW9ucyBpbiB0aGUgc2FtcGxlDQoNCiRuIFxnZXEgMzAkDQoNCiMjIyMgKipQYWlyZWQgc2FtcGxlcyoqDQoNCi0gICBBdCBsZWFzdCAzMCBwYWlycyBvZiBvYnNlcnZhdGlvbnMgYWNyb3NzIHRoZSBzYW1wbGVzDQoNCiROdW1iZXJzIFwgb2YgXCByb3dzIFwgaW4gXCB5b3VyIFwgZGF0YSBcZ2VxIDMwJA0KDQojIyMjICoqVHdvIHNhbXBsZXMqKg0KDQotICAgQXQgbGVhc3QgMzAgb2JzZXJ2YXRpb25zIGluIGVhY2ggc2FtcGxlDQoNCiRuX3sxfSBcZ2VxIDMwIFwgLCBuX3syfSBcZ2VxIDMwJA0KDQojIyMjICoqQU5PVkEqKg0KDQotICAgQXQgbGVhc3QgcGFpcnMgb2YgMzAgb2JzZXJ2YXRpb25zIGluIGVhY2ggc2FtcGxlDQoNCiRuX3tpfSBcZ2VxIDMwIGZvciBcIGFsbCBcIHZhbHVlcyBcIG9mIFwgaSQNCg0KIyMjICoqTGFyZ2Ugc2FtcGxlIHNpemU6IHByb3BvcnRpb24gdGVzdHMqKg0KDQpGb3Igb25lIHNhbXBsZSBwcm9wb3J0aW9uIHRlc3RzLCAqKnRoZSBzYW1wbGUgaXMgY29uc2lkZXJlZCBiaWcgZW5vdWdoIGlmIGl0IGNvbnRhaW5zIGF0IGxlYXN0IHRlbiBzdWNjZXNzZXMgYW5kIHRlbiBmYWlsdXJlcyoqLiBOb3RpY2UgdGhhdCBpZiB0aGUgcHJvYmFiaWxpdHkgb2Ygc3VjY2VzcyBpcyBjbG9zZSB0byB6ZXJvIG9yIGNsb3NlIHRvIG9uZSwgdGhlbiB5b3UgbmVlZCBhIGJpZ2dlciBzYW1wbGUuIEluIHRoZSB0d28gc2FtcGxlIGNhc2UgdGhlIHNpemUgcmVxdWlyZW1lbnRzIGFwcGx5IHRvIGVhY2ggc2FtcGxlIHNlcGFyYXRlbHkuDQoNCiMjIyMgKipPbmUgc2FtcGxlKioNCg0KLSAgIE51bWJlciBvZiBzdWNjZXNzZXMgaW4gc2FtcGxlIGlzIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byAxMC4NCg0KJG4gXHRpbWVzIFxoYXR7cH0gXGdlcSAxMCQNCg0KLSAgIE51bWJlciBvZiBmYWlsdXJlcyBpbiBzYW1wbGUgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDEwLg0KDQokbiBcdGltZXMgKDEgLSBcaGF0e3B9KSBcZ2VxIDEwJA0KDQokbjokIFNhbXBsZSBzaXplDQoNCiRcaGF0e3B9OiQgUHJvcG9ydGlvbiBvZiBzdWNjZXNzZXMgb24gc2FtcGxlDQoNCiMjIyMgKipUd28gc2FtcGxlcyoqDQoNCi0gICBOdW1iZXIgb2Ygc3VjY2Vzc2VzIGluIGVhY2ggc2FtcGxlIGlzIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byAxMC4NCg0KJG5fezF9IFx0aW1lcyBcaGF0e3B9X3sxfSBcZ2VxIDEwJA0KDQokbl97Mn0gXHRpbWVzIFxoYXR7cH1fezJ9IFxnZXEgMTAkDQoNCi0gICBOdW1iZXIgb2YgZmFpbHVyZXMgaW4gZWFjaCBzYW1wbGUgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDEwDQoNCiRuX3sxfSBcdGltZXMgKDEgLSBcaGF0e3B9X3sxfSkgXGdlcSAxMCQNCg0KJG5fezJ9IFx0aW1lcyAoMSAtIFxoYXR7cH1fezJ9KSBcZ2VxIDEwJA0KDQojIyMgKipMYXJnZSBzYW1wbGUgc2l6ZTogY2hpLXNxdWFyZSB0ZXN0cyoqDQoNClRoZSBjaGktc3F1YXJlIHRlc3QgaXMgc2xpZ2h0bHkgbW9yZSBmb3JnaXZpbmcgYW5kIG9ubHkgcmVxdWlyZXMgZml2ZSBzdWNjZXNzZXMgYW5kIGZhaWx1cmVzIGluIGVhY2ggZ3JvdXAgcmF0aGVyIHRoYW4gdGVuLg0KDQotICAgVGhlIG51bWJlciBvZiBzdWNjZXNzZXMgaW4gZWFjaCBncm91cCBpcyBncmVhdGVlciB0aGFuIG9yIGVxdWFsIHRvIDUNCg0KJG5fe2l9IFx0aW1lcyBcaGF0e3B9X3tpfSBcZ2VxIDUkIGZvciBhbGwgdmFsdWVzIGluICRpJA0KDQotICAgVGhlIG51bWJlciBvZiBmYWlsdXJlcyBpbiBlYWNoIGdyb3VwIGlzIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byA1DQoNCiRuX3tpfSBcdGltZXMgKDEgLSBcaGF0e3B9X3tpfSkgXGdlcSA1JCBmb3IgYWxsIHZhbHVlcyBpbiAkaSQNCg0KJG5fe2l9OiQgc2FtcGxlIHNpemUgZm9yIGdyb3VwICRpJA0KDQokXGhhdHtwfV97aX06JCBwcm9wb3J0aW9uIG9mIHN1Y2Nlc3NlcyBpbiBzYW1wbGUgZ3JvdXAgJGkkDQoNCiMjIyAqKlNhbml0eSBjaGVjayoqDQoNCioqT25lIG1vcmUgY2hlY2sgeW91IGNhbiBwZXJmb3JtIGlzIHRvIGNhbGN1bGF0ZSBhIGJvb3RzdHJhcCBkaXN0cmlidXRpb24gYW5kIHZpc3VhbGl6ZSBpdCB3aXRoIGEgaGlzdG9ncmFtKiouIElmIHlvdSBkb24ndCBzZWUgYSBiZWxsLXNoYXBlZCBub3JtYWwgY3VydmUsIHRoZW4gb25lIG9mIHRoZSBhc3N1bXB0aW9ucyBoYXNuJ3QgYmVlbiBtZXQuIEluIHRoYXQgY2FzZSwgeW91IHNob3VsZCByZXZpc2l0IHRoZSBkYXRhIGNvbGxlY3Rpb24gcHJvY2VzcywgYW5kIHNlZSBpZiBhbnkgb2YgdGhlIHRocmVlIGFzc3VtcHRpb25zIG9mIHJhbmRvbW5lc3MsIGluZGVwZW5kZW5jZSwgYW5kIHNhbXBsZSBzaXplIGRvIG5vdCBob2xkLg0KDQojIyMgU2VwZWNpZnlpbmcgYW5kIGh5cG90aGVzaXppbmcNCg0KwqBSZWNhbGwgdGhlIGh5cG90aGVzZXMuDQoNCiQkDQpIX3swfTogbGF0ZV97ZXhwZW5zaXZlfSBcIC0gbGF0ZV97cmVhc29uYWJsZX0gXCA9IDANClxcIEhfe0F9OiBsYXRlX3tleHBlbnNpdmV9IFwgLSBsYXRlX3tyZWFzb25hYmxlfSBcID4gMA0KJCQNCg0KTGV0J3MgY29tcGFyZSB0aGF0IHRyYWRpdGlvbmFsIGFwcHJvYWNoIHVzaW5nwqBgcHJvcF90ZXN0KClgwqB3aXRoIGEgc2ltdWxhdGlvbi1iYXNlZCBpbmZlciBwaXBlbGluZS4NCg0KYGBge3J9DQojIFNwZWNpZnkgdGhldCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBsYXRlIHByb3BvcnRpb25zIGFjcm9zcyBmcmVpZ2h0X2Nvc3RfZ3JvdXBzLCB3aGVyZSAiWWVzIiBkZW5vdGVzIHN1Y2Nlc3MNCnNwZWNpZmllZCA8LSBsYXRlX3NoaXBtZW50cyAlPiUNCiAgc3BlY2lmeShsYXRlIH4gZnJlaWdodF9jb3N0X2dyb3VwLCBzdWNjZXNzID0gIlllcyIpDQoNCiMgU2VlIHRoZSByZXN1bHQNCnNwZWNpZmllZA0KYGBgDQoNCmBgYHtyfQ0KIyBFeHRlbmQgdGhlIHBpcGVsaW5lIHRvIGRlY2xhcmUgdGhlIG51bGwgaHlwb3RoZXNpcyB0aGF0IHRoZSB2YXJpYWJsZXMgYXJlIGluZGVwZW5kZW50DQpoeXBvdGhlc2l6ZWQgPC0gbGF0ZV9zaGlwbWVudHMgJT4lDQogIHNwZWNpZnkobGF0ZSB+IGZyZWlnaHRfY29zdF9ncm91cCwgc3VjY2VzcyA9ICJZZXMiKSAlPiUNCiAgaHlwb3RoZXNpemUobnVsbCA9ICJpbmRlcGVuZGVuY2UiKQ0KDQojIFNlZSB0aGUgcmVzdWx0DQpoeXBvdGhlc2l6ZWQNCmBgYA0KDQojIyMgKipHZW5lcmF0aW5nICYgY2FsY3VsYXRpbmcqKg0KDQpUaGUgaW5mZXIgcGlwZWxpbmUgZm9yIGh5cG90aGVzaXMgdGVzdGluZyByZXF1aXJlcyBmb3VyIHN0ZXBzIHRvIGNhbGN1bGF0ZSB0aGUgbnVsbCBkaXN0cmlidXRpb246IHNwZWNpZnksIGh5cG90aGVzaXplLCBnZW5lcmF0ZSwgYW5kIGNhbGN1bGF0ZS4NCg0KTGV0J3MgY29udGludWUgdGhlIHBpcGVsaW5lIHlvdSBiZWdhbiBpbiB0aGUgcHJldmlvdXMgY29kaW5nIGV4ZXJjaXNlLiBXZSdsbCBnZXQgYSBzZXQgb2YgZGlmZmVyZW5jZXMgaW4gcHJvcG9ydGlvbnMgdGhhdCBhcmUgZGlzdHJpYnV0ZWQgYXMgdGhvdWdoIHRoZSBudWxsIGh5cG90aGVzaXMsIHRoYXQgdGhlIHByb3BvcnRpb24gb2YgbGF0ZSBzaGlwbWVudHMgaXMgdGhlIHNhbWUgYWNyb3NzIGZyZWlnaHQgY29zdCBncm91cHMsIGlzIHRydWUuDQoNCmBgYHtyfQ0KIyBFeHRlbmQgdGhlIHBpcGVsaW5lIHRvIGdlbmVyYXRlIDIwMDAgcGVybXV0YXRpb25zDQpnZW5lcmF0ZWQgPC0gbGF0ZV9zaGlwbWVudHMgJT4lDQogIHNwZWNpZnkobGF0ZSB+IGZyZWlnaHRfY29zdF9ncm91cCwgc3VjY2VzcyA9ICJZZXMiKSAlPiUNCiAgaHlwb3RoZXNpemUobnVsbCA9ICJpbmRlcGVuZGVuY2UiKSAlPiUNCiAgZ2VuZXJhdGUocmVwcyA9IDIwMDAsIHR5cGUgPSAicGVybXV0ZSIpDQoNCiMgU2VlIHRoZSByZXN1bHRzIA0KZ2VuZXJhdGVkDQpgYGANCg0KYGBge3J9DQojIEV4dGVuZCB0aGUgcGlwZWxpbmUgdG8gY2FjdWxhdGUgdGhlIGRpZmZlcmVuY2UgaW4gcHJvcG9ydGlvbnMgKGV4cGVuc2l2ZSBtaW51cyByZWFzb25hYmxlKQ0KbnVsbF9kaXN0biA8LSBsYXRlX3NoaXBtZW50cyAlPiUNCiAgc3BlY2lmeShsYXRlIH4gZnJlaWdodF9jb3N0X2dyb3VwLCBzdWNjZXNzID0gIlllcyIpICU+JQ0KICBoeXBvdGhlc2l6ZShudWxsID0gImluZGVwZW5kZW5jZSIpICU+JQ0KICBnZW5lcmF0ZShyZXBzID0gMjAwMCwgdHlwZSA9ICJwZXJtdXRlIikgJT4lDQogIGNhbGN1bGF0ZShzdGF0ID0gImRpZmYgaW4gcHJvcHMiLCBvcmRlciA9IGMoImV4cGVuc2l2ZSIsICJyZWFzb25hYmxlIikpDQoNCm51bGxfZGlzdG4NCmBgYA0KDQpgYGB7cn0NCnZpc3VhbGl6ZShudWxsX2Rpc3RuKQ0KYGBgDQoNCiMjIyAqKk9ic2VydmVkIHN0YXRpc3RpYyBhbmQgcC12YWx1ZSoqDQoNCllvdSBub3cgaGF2ZSBhIG51bGwgZGlzdHJpYnV0aW9uLiBJbiBvcmRlciB0byBnZXQgYSBwLXZhbHVlIGFuZCB3ZWlnaCB1cCB0aGUgZXZpZGVuY2UgYWdhaW5zdCB0aGUgbnVsbCBoeXBvdGhlc2lzLCB5b3UgbmVlZCB0byBjYWxjdWxhdGUgdGhlIGRpZmZlcmVuY2UgaW4gcHJvcG9ydGlvbnMgdGhhdCBpcyBvYnNlcnZlZCBpbiB0aGUgYGxhdGVfc2hpcG1lbnRzYCBzYW1wbGUuDQoNCmBgYHtyfQ0KIyBDb3B5LCBwYXN0ZSAmIG1vZGlmeSB0aGUgcGlwZWxpbmUgdG8gZ2V0IHRoZSBvYnNlcnZlZCBzdGF0aXN0aWMNCm9ic19zdGF0IDwtIGxhdGVfc2hpcG1lbnRzICU+JQ0KICBzcGVjaWZ5KGxhdGUgfiBmcmVpZ2h0X2Nvc3RfZ3JvdXAsIHN1Y2Nlc3MgPSAiWWVzIikgJT4lDQogIGh5cG90aGVzaXplKG51bGwgPSAiaW5kZXBlbmRlbmNlIikgJT4lDQogIGNhbGN1bGF0ZShzdGF0ID0gImRpZmYgaW4gcHJvcHMiLCBvcmRlciA9IGMoImV4cGVuc2l2ZSIsICJyZWFzb25hYmxlIikpDQoNCiMgU2VlIHRoZSByZXN1bHRzDQpvYnNfc3RhdA0KYGBgDQoNCmBgYHtyfQ0KIyBWaXN1YWxpemUgdGhlIG51bGwgZGlzdMK0biwgYWRkaW5nIGEgdmVydGljYWwgbGluZSB0byB0aGUgb2JzZXJ2ZWQgc3RhdGlzdGljDQp2aXN1YWxpemUobnVsbF9kaXN0bikgKyANCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IHN0YXQpLCBkYXRhPW9ic19zdGF0LCBjb2xvciA9ICJyZWQiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBHZXQgdGhlIHAtdmFsdWUNCnBfdmFsdWUgPC0gZ2V0X3BfdmFsdWUobnVsbF9kaXN0biwgb2JzX3N0YXQsIGRpcmVjdGlvbiA9ICJ0d28gc2lkZWQiKQ0KDQojIFNlZSB0aGUgcmVzdWx0DQpwX3ZhbHVlDQpgYGANCg0KIyMjICoqU2ltdWxhdGlvbi1iYXNlZCB0LXRlc3QqKg0KDQpXIG1hbnVhbGx5IHBlcmZvcm1lZCB0aGUgc3RlcHMgZm9yIGEgdC10ZXN0IHRvIGV4cGxvcmUgdGhlc2UgaHlwb3RoZXNlcy4NCg0KJEhfezB9JCA6IFRoZSBtZWFuIHdlaWdodCBvZiBzaGlwbWVudHMgdGhhdCB3ZXJlbid0IGxhdGUgaXMgdGhlIHNhbWUgYXMgdGhlIG1lYW4gd2VpZ2h0IG9mIHNoaXBtZW50cyB0aGF0IHdlcmUgbGF0ZS4NCg0KJEhfe0F9JCA6IFRoZSBtZWFuIHdlaWdodCBvZiBzaGlwbWVudHMgdGhhdCB3ZXJlbid0IGxhdGUgaXMgbGVzcyB0aGFuIHRoZSBtZWFuIHdlaWdodCBvZiBzaGlwbWVudHMgdGhhdCB3ZXJlIGxhdGUuDQoNCllvdSBjYW4gcnVuIHRoZSB0ZXN0IG1vcmUgY29uY2lzZWx5IHVzaW5nIGluZmVyJ3MgYHRfdGVzdCgpYC4NCg0KYHRfdGVzdCgpYMKgYXNzdW1lcyB0aGF0IHRoZSBudWxsIGRpc3RyaWJ1dGlvbiBpcyBub3JtYWwuIFdlIGNhbiBhdm9pZCBhc3N1bXB0aW9ucyBieSB1c2luZyBhIHNpbXVsYXRpb24tYmFzZWQgbm9uLXBhcmFtZXRyaWMgZXF1aXZhbGVudC4NCg0KYGBge3J9DQojIEZpbGwgb3V0IHRoZSBudWxsIGRpc3RyaWJ1dGlvbiBwaXBlbGluZQ0KbnVsbF9kaXN0biA8LSBsYXRlX3NoaXBtZW50cyAlPiUNCiAgIyBTcGVjaWZ5IHdlaWdodF9raWxvZ3JhbXMgdnMuIGxhdGUgDQogIHNwZWNpZnkod2VpZ2h0X2tpbG9ncmFtcyB+IGxhdGUpICU+JQ0KICAjIERlY2xhcmUgYSBudWxsIGh5cG90aGVzaXMgb2YgaW5kZXBlbmRlbmNlIA0KICBoeXBvdGhlc2l6ZShudWxsID0gImluZGVwZW5kZW5jZSIpICU+JQ0KICAjIEdlbmVyYXRlIGEgMTAwIHBlcm11dGF0aW9uIHJlcGxpY2F0ZXMNCiAgZ2VuZXJhdGUocmVwcyA9IDEwMDAsIHR5cGUgPSAicGVybXV0ZSIpICU+JQ0KICAjIENhbGN1bGF0ZSB0aGUgZGlmZmVyZW5jZSBpbiBtZWFucyAoIk5vIiwgIm1pdW5zIiwgIlllcyIpDQogIGNhbGN1bGF0ZShzdGF0ID0gImRpZmYgaW4gbWVhbnMiLCBvcmRlciA9IGMoIk5vIiwgIlllcyIpKQ0KDQojIFNlZSB0aGUgcmVzdWx0cw0KbnVsbF9kaXN0bg0KYGBgDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIG9ic2VydmVkIGRpZmZlcmVuY2UgaW4gbWVhbnMgDQpvYnNfc3RhdCA8LSBsYXRlX3NoaXBtZW50cyAlPiUNCiAgc3BlY2lmeSh3ZWlnaHRfa2lsb2dyYW1zIH4gbGF0ZSkgJT4lDQogIGNhbGN1bGF0ZShzdGF0ID0gImRpZmYgaW4gbWVhbnMiLCBvcmRlciA9IGMoIk5vIiwgIlllcyIpKQ0KDQojIFNlZSB0aGUgcmVzdWx0cw0Kb2JzX3N0YXQNCmBgYA0KDQpgYGB7cn0NCiMgR2V0IHRoZSBwLXZhbHVlDQpwX3ZhbHVlIDwtIGdldF9wX3ZhbHVlKG51bGxfZGlzdG4sIG9ic19zdGF0LCBkaXJlY3Rpb24gPSAibGVzcyIpDQoNCiMgU2VlIHRoZSByZXN1bHQNCnBfdmFsdWUNCmBgYA0KDQojIyMgKipSYW5rIHN1bSB0ZXN0cyoqDQoNCkFub3RoZXIgY2xhc3Mgb2Ygbm9uLXBhcmFtZXRyaWMgaHlwb3RoZXNpcyB0ZXN0cyBhcmUgY2FsbGVkICoqKnJhbmsgc3VtIHRlc3RzKioqLiAqKlJhbmtzIGFyZSB0aGUgcG9zaXRpb25zIG9mIG51bWVyaWMgdmFsdWVzIGZyb20gc21hbGxlc3QgdG8gbGFyZ2VzdCoqLiBUaGluayBvZiB0aGVtIGFzIHBvc2l0aW9ucyBpbiBydW5uaW5nIGV2ZW50czogd2hvZXZlciBoYXMgdGhlIGZhc3Rlc3QgKHNtYWxsZXN0KSB0aW1lIGlzIHJhbmsgMSwgc2Vjb25kIGZhc3Rlc3QgaXMgcmFuayAyLCBhbmQgc28gb24uDQoNCkJ5IGNhbGN1bGF0aW5nIG9uIHRoZSByYW5rcyBvZiBkYXRhIGluc3RlYWQgb2YgdGhlIGFjdHVhbCB2YWx1ZXMsIHlvdSBjYW4gYXZvaWQgbWFraW5nIGFzc3VtcHRpb25zIGFib3V0IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHRlc3Qgc3RhdGlzdGljLiBJdCdzIG1vc3Qgcm9idXN0IGluIHRoZSBzYW1lIHdheSB0aGF0IGEgbWVkaWFuIGlzIG1vcmUgcm9idXN0IHRoYW4gYSBtZWFuLg0KDQpUd28gY29tbW9ubHkgdXNlZCByYW5rLWJhc2VkIHRlc3RzIGFyZSB0aGUgV2lsY294b24tTWFubi1XaGl0bmV5IHRlc3QsIHdoaWNoIGlzIGxpa2UgYSBub24tcGFyYW1ldHJpYyB0LXRlc3QsIGFuZCB0aGUgS3J1c2thbC1XYWxsaXMgdGVzdCwgd2hpY2ggaXMgbGlrZSBhIG5vbi1wYXJhbWV0cmljIEFOT1ZBLg0KDQpgYGB7cn0NCiMgUnVuIGEgV2lsY294b24tTWFubi1XaGl0bmV5IHRlc3Qgb24gd2VpZ2h0X2tpbG9ncmFtcyB+IGxhdGUNCnRlc3RfcmVzdWx0cyA8LSAgIHdpbGNveC50ZXN0KHdlaWdodF9raWxvZ3JhbXMgfiBsYXRlLCBkYXRhID0gbGF0ZV9zaGlwbWVudHMpDQoNCiMgU2VlIHRoZSByZXN1bHRzDQp0ZXN0X3Jlc3VsdHMNCmBgYA0KDQpgYGB7cn0NCiMgUnVuIGEgS3J1c2thbC1XYWxsYWNlIHRlc3Qgb24gd2VpZ2h0X2tpbG9ncmFtcyB2cy4gc2hpcG1lbnRfbW9kZSANCnRlc3RfcmVzdWx0cyA8LSBrcnVza2FsLnRlc3Qod2VpZ2h0X2tpbG9ncmFtcyB+IHNoaXBtZW50X21vZGUsIGRhdGEgPSBsYXRlX3NoaXBtZW50cykNCg0KIyBTZWUgdGhlIHJlc3VsdHMNCnRlc3RfcmVzdWx0cw0KYGBgDQoNCiMjIyAqKkVORCoqDQo=