Outline


    01. Comparing two groups (t-tests)

    02. Comparing three and more groups (ANOVA)


    Data and packages

    Housing in Brno


    • Packages:
    require("readxl")
    require("dplyr")
    require("summarytools")
    require("ggplot2")
    library("psych")
    library(sjstats)
    library("car")


    • Data:
      Bydleni_Brno <- read_excel("Bydleni_Brno.xlsx")


    Comparing two groups


    Dependent t-test



    Assumptions


    • The sampling distribution is normally distributed.
      • In the dependent t-test this means that the sampling distribution of the differences between scores should be normal, not the scores themselves.
    • Data (“Dependent variable”) are measured at least at the interval level.


    Main Part of the Analysis


    • In the case of our dependent t-test, we need to specify these arguments to t.test():
     ?t.test


    • “paired=…”: Whether we’re doing a dependent (i.e. paired) t-test or independent t-test.
      • In case of the paired t-test it’s TRUE.
    • Note that t.test() carries out a two-sided t-test by default.


    • Variables:
      • x: Column of Bydleni_Brno containing prices for 2015.
      • y: Column of Bydleni_Brno containing prices for 2016


    • Conduct a paired t-test using the t.test function:
    t.test(Bydleni_Brno$Pronajem_m2_2015, Bydleni_Brno$Pronajem_m2_2016, mu = -1,
           paired = TRUE)
    
        Paired t-test
    
    data:  Bydleni_Brno$Pronajem_m2_2015 and Bydleni_Brno$Pronajem_m2_2016
    t = -2.4345, df = 28, p-value = 0.02154
    alternative hypothesis: true difference in means is not equal to -1
    95 percent confidence interval:
     -19.477518  -2.591448
    sample estimates:
    mean of the differences 
                  -11.03448 


    Effect Size


    • Packages:
    library("lsr")
    library("effsize")


    • Cohen’s d, the effsize way:
    cohen.d(x, y, 
            pooled=TRUE, 
            paired=TRUE,
            na.rm=FALSE, 
            hedges.correction=FALSE,
            conf.level=0.95, 
            noncentral=FALSE)
    
    ?cohen.d()


    cohen.d(Bydleni_Brno$Pronajem_m2_2015, Bydleni_Brno$Pronajem_m2_2016, paired=TRUE)
    
    Cohen's d
    
    d estimate: -0.2324419 (small)
    95 percent confidence interval:
          lower       upper 
    -0.40870688 -0.05617685 


    Visualisation of the Cohen’s d


    • This app will show you a graph of simulated data with a random number of observations in each of two groups and random effect size.
      • The effect size will be between -3 and 3, and your job is to guess the size of the effect.


    Independent t-test



    Assumptions


    • Homogeneity of variance.
    • Scores are independent (because they come from non-identical units).
    • Data (“Dependent variable”) are measured at least at the interval level.


    • Create subsets:
    Sidliste <- Bydleni_Brno %>%
     select(c("Sidliste", "Pronajem_m2_2016")) %>%
     filter(Sidliste == 1, Pronajem_m2_2016 > 0)
    
    Rodinne_Domy <- Bydleni_Brno %>%
     select(c("Sidliste", "Pronajem_m2_2016")) %>%
     filter(Sidliste == 0, Pronajem_m2_2016 > 0)


    • Summary statistics:
    Sidliste_view <- view(dfSummary(Sidliste))
    
    Rodinne_Domy_view <- view(dfSummary(Rodinne_Domy))


    • Visualise the data:
    Bydleni_Brno_Najem <- Bydleni_Brno %>%
     filter(Pronajem_m2_2016 > 0) %>%
     ggplot(aes(x = factor(Sidliste), 
                y = Pronajem_m2_2016, 
                fill = factor(Sidliste))) + 
      geom_boxplot()
    
    Bydleni_Brno_Najem


    Levene’s test (Load the car package):

    # Wrangle the data:
    Bydleni_Brno_Najem_Levene <- Bydleni_Brno %>%
     filter(Pronajem_m2_2016 > 0)
    
    # Levene's test
    leveneTest(Bydleni_Brno_Najem_Levene$Pronajem_m2_2016 ~ factor(Bydleni_Brno_Najem_Levene$Sidliste))
    Levene's Test for Homogeneity of Variance (center = median)
          Df F value  Pr(>F)  
    group  1  7.4252 0.01157 *
          25                  
    ---
    Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1


    • Conduct an independent t-test:
      t.test(Sidliste$Pronajem_m2_2016, Rodinne_Domy$Pronajem_m2_2016,
             var.equal = FALSE)
    
        Welch Two Sample t-test
    
    data:  Sidliste$Pronajem_m2_2016 and Rodinne_Domy$Pronajem_m2_2016
    t = -0.98247, df = 9.591, p-value = 0.35
    alternative hypothesis: true difference in means is not equal to 0
    95 percent confidence interval:
     -37.18558  14.51891
    sample estimates:
    mean of x mean of y 
     149.7778  161.1111 


    Size Effect



    • Cohen’s d, the lsr way:
    # Data
    Najem_2015 <- Bydleni_Brno %>% select(Pronajem_m2_2015)
    Sidliste_2015 <- Bydleni_Brno %>% select(Sidliste)
    
    
    cohensD(Bydleni_Brno$Pronajem_m2_2015 ~ Bydleni_Brno$Sidliste)
    [1] 0.4811341


    • Cohen’s d, the effsize way:
    cohen.d(Bydleni_Brno$Pronajem_m2_2015 ~ Bydleni_Brno$Sidliste)
    Cohercing rhs of formula to factor
    
    Cohen's d
    
    d estimate: -0.4811341 (small)
    95 percent confidence interval:
         lower      upper 
    -1.2770112  0.3147431 

    Comparing three and more groups

    One-way ANOVA


    Introduction


    ANOVA = ANalysis Of VAriance


    • Used when comparing group means of three and more groups/conditions.
    • Two default variants:
      • Between design: independent samples (ANOVA, ANCOVA, factorial ANOVA, etc.)
        • Is there any difference between regions when it comes to the income in the Czech Republic?
    • Within design: a comparison of a group mean across several points in time (Repeated Measures ANOVA).
      • Have households increased fuel consumption during the last five years?


    “Grammar”


    • Factor - the most frequent attained level of education in the respective city district (primary, secondary, tertiary)
    • Dependent variable - the average price for an apartment (60 square metres) in the respective city district in 2016.
    • Null hypothesis:
      • All of the groups have similar mean. Alternatively, there is no difference in the mean value of an apartment (60 square metres) in Brno based on the most frequent level of education in the respective city district.
    • Alternative hypothesis:
      • The average price for a flat of 60 square metres is the highest in the districts with university alumni as the most frequent category of the level of education.


    F-test and F-ratio

    • Null hypothesis: all groups are equal
      • ANOVA provides a significance test
    • Test statistic is the F-test (or F-ratio)


    • How do we get the associated p-value?
      • The “family” of F-distributions is based on:
        • The number of observations (cases or simply n)
        • The number of compared conditions (groups)


    Simulation of the F-distribution

    # Create the vector x
    x <- seq(from = 0, to = 10, length = 2000)
    
    # Evaluate the densities
    y_1 <- df(x, 3, 100)
    y_2 <- df(x, 1, 1)
    y_3 <- df(x, 2, 100)
    y_4 <- df(x, 3, 30)
    y_5 <- df(x, 3, 500)
    y_6 <- df(x, 3, 50)
    y_7 <- df(x, 6, 1000)
    
    # Plot the densities
    plot(x, y_1, col = 1, type = "l")
    lines(x, y_2, col = 2)
    lines(x, y_3, col = 3)
    lines(x, y_4, col = 4)
    lines(x, y_5, col = 5)
    lines(x, y_6, col = 6)
    lines(x, y_7, col = 7)
    
    # Add the legend
    legend("topright", title = "F distributions",
    c("df = (3, 100)", "df = (1, 1)", "df = (2, 100)", "df = (3, 30)",
    "df = (3, 500)", "df = (3, 50)", "df = (6, 1000)"),
    col = c(1, 2, 3, 4, 5, 6, 7), lty = 1)


    Summary Table



    Code


    • Data wrangling
    Bydleni_Brno$Vzdelani = factor(Bydleni_Brno$Vzdelani, 
                                   order = TRUE, 
                                   levels = c(1, 2, 3),
                                   labels = c("Základní", 
                                             "Středoškolské", 
                                             "Vysokoškolské"))
    
    Bydleni_Brno_60m2_2016 <- Bydleni_Brno %>%
                                filter(Prodej_60m2_2016 > 0)


    • Summary statistics by groups:
    describeBy(Bydleni_Brno_60m2_2016$Prodej_60m2_2016, 
               group = factor(Bydleni_Brno_60m2_2016$Vzdelani))
    
     Descriptive statistics by group 
    group: Základní
    --------------------------------------------------------------------------------------------- 
    group: Středoškolské
    --------------------------------------------------------------------------------------------- 
    group: Vysokoškolské


    • Boxplot:
    bp1 = ggplot(Bydleni_Brno_60m2_2016, aes(Vzdelani, Prodej_60m2_2016))
    bp1 + geom_boxplot(aes(fill=Vzdelani), alpha=I(0.5)) +
     geom_point(position="jitter", alpha=0.5) +
     geom_boxplot(outlier.size=0, alpha=0.5) +
     theme(
     axis.title.x = element_text(face="bold", color="black", size=12),
     axis.title.y = element_text(face="bold", color="black", size=12),
     plot.title = element_text(face="bold", color = "black", size=12)) +
     labs(x="Převažující kategorie vzdělání ",
     y = "Cena za byt o rozloze 60 metrů čtverečních (v Kč)",
     title= "Cena za byt o rozloze 60 metrů čtverečních (v Kč) dle převažující kategorie vzdělání") +
    theme(legend.position='none')
    


    • The aov function:
    aov(dependent_var ~ independent_var)
    summary()


    • Apply the aov function:
    anova_BydleniBrno <- aov(Prodej_60m2_2016 ~ Vzdelani, 
                             data = Bydleni_Brno_60m2_2016)


    • Look at the summary table of the result
    summary(anova_BydleniBrno)
                Df    Sum Sq   Mean Sq F value Pr(>F)
    Vzdelani     2 3.465e+11 1.733e+11   0.963  0.396
    Residuals   23 4.137e+12 1.799e+11               


    Size effect


    • The lsr package:
    library(lsr)
    etaSquared(anova_BydleniBrno, 
               type = 2,
               anova = FALSE)
                 eta.sq eta.sq.part
    Vzdelani 0.07729335  0.07729335



    • The sjstats package:
    anova <- lm(Prodej_60m2_2016 ~ Vzdelani, 
                    data = Bydleni_Brno_60m2_2016)
    performance::r2(anova)
    # R2 for Linear Regression
    
           R2: 0.077
      adj. R2: -0.003




    Assumptions


    Variables

    • “Dependent” variable at least on the interval level.


    Normal distribution of the dependent variable

    • In each of the groups/conditions.
    • Violation of this assumption should not substantially bias the test result as long as the groups have a similar number of observations, and in each group, there are at least 30 observations.
    • Non-parametric alternative – Kruskal-Wallis test


    Homogenity of variance

    • Sledujeme Levenův F-test, nulová hypotéza hovoří o homogenitě napříč skupinami
    • Pokud Levenův F-test vychází statisticky signifikantní:
    • Sledujeme poměr rozptylu u skupin s největším a nejmenším rozptylem, přičemž chceme, aby byl tento poměr menší než 3
    • Narušení by nemělo vadit, pokud jsou skupiny stejně velké
    • Při narušení lze použít Welchovo F


    Independence of observation


    Homogenity of variance
    library("car")


    • If you don’t specify additional arguments, the deviation scores are calculated by comparing each score to its group median.
      • This is the default behaviour, even though they are typically calculated by comparing each score to its group mean.
      • If you want to use means and not medians, add an argument center = mean. Do this now and compare the results to the first test.


    • Levene’s test:
    leveneTest(Prodej_60m2_2016 ~ Vzdelani, data = Bydleni_Brno_60m2_2016)
    Levene's Test for Homogeneity of Variance (center = median)
          Df F value Pr(>F)
    group  2  0.7446  0.486
          23               


    • Levene’s test with center = mean:
    leveneTest(Prodej_60m2_2016 ~ Vzdelani, data = Bydleni_Brno_60m2_2016, center = mean)
    Levene's Test for Homogeneity of Variance (center = mean)
          Df F value Pr(>F)
    group  2  0.8418 0.4438
          23               


    Normality of the dependent variable:
    ggplot(data=Bydleni_Brno_60m2_2016, aes(Prodej_60m2_2016)) +
     geom_histogram(binwidth = 250000, col="red",
     aes(fill=..count..)) +
     scale_fill_gradient("Count", low = "green", high = "red") +
     labs(title="Histogram ceny za byt o rozloze 60 metrů čtverečních v Brně v roce 2016") +
     labs(x="Cena", y="Četnost") + theme(legend.position='none')


    F-test

    • Fischer’s test (F-test):
    anova_wm_VNE = oneway.test(Prodej_60m2_2016 ~ Vzdelani, 
                               data = Bydleni_Brno_60m2_2016, 
                               var.equal=TRUE)
    
    anova_wm_VNE
    
        One-way analysis of means
    
    data:  Prodej_60m2_2016 and Vzdelani
    F = 0.96333, num df = 2, denom df = 23, p-value = 0.3965


    • Welch’s F-test:
    anova_wm_VE = oneway.test(Prodej_60m2_2016 ~ Vzdelani, 
                              data = Bydleni_Brno_60m2_2016, 
                              var.equal=FALSE)
    
    anova_wm_VE
    
        One-way analysis of means (not assuming equal variances)
    
    data:  Prodej_60m2_2016 and Vzdelani
    F = 0.68429, num df = 2.0000, denom df = 5.6794, p-value = 0.5417


    Post-Hoc Tests


    Allow for multiple pairwise comparisons without an increase in the probability of a Type I error.

    Používáme, pokud nemáme dopředu jasné hypotézy.

    • Srovnávají vše se vším – každou skupinu s každou (ale neumí slučovat skupiny jako kontrasty).
      • Z principu jsou oboustranné.
    • Je jich mnoho – liší se v několika parametrech:
      • Konzervativní (Ch. II. typu) versus Liberální (Ch. I. typu),
      • Most liberal = no adjustment,
      • Most conservative = adjust for every possible comparison that could be made,
      • Ne/vhodné pro rozdílně velké skupiny,
      • Ne/vhodné pro rozdílné skupinové rozptyly.


    Recommendation by A. Field:


    • Stejně velké skupiny a skupinové rozptyly (ideální situace):
      • REGWQ
      • Tukey


    • Pokud si chceme být jistí, že P chyby I. typu nepřekročí zvolenou hladinu:
      • Bonferroni


    • Pokud jsou velikosti skupin trochu/hodně rozdílné:
      • Gabriel
      • Hochberg GT2


    • Pokud pochybujeme o shodnosti skupinových rozptylů:
      • Games-Howell


    Tukey


    • Conduct ANOVA:
    anova_BydleniBrno = aov(Prodej_60m2_2016 ~ Vzdelani, data = Bydleni_Brno_60m2_2016)


    • View summary:
    summary(anova_BydleniBrno)
                Df    Sum Sq   Mean Sq F value Pr(>F)
    Vzdelani     2 3.465e+11 1.733e+11   0.963  0.396
    Residuals   23 4.137e+12 1.799e+11               


    • Conduct Tukey procedure:
    tukey <- TukeyHSD(anova_BydleniBrno)


    • Plot confidence intervals:
    plot(tukey)


    Bonferroni


    The Bonferroni correction compensates for that increase by testing each individual hypothesis at a significance level of α/m, where α is the desired overall alpha level and m is the number of hypotheses.

    • For example, if a trial is testing m = 20 hypotheses with a desired α = 0.05, then the Bonferroni correction would test each individual hypothesis at α = 0.05/20 = 0.0025.


    • Pairwise t-test:
    pairwise.t.test(Bydleni_Brno_60m2_2016$Prodej_60m2_2016,
    Bydleni_Brno_60m2_2016$Vzdelani, p.adjust = "bonferroni")
    
        Pairwise comparisons using t tests with pooled SD 
    
    data:  Bydleni_Brno_60m2_2016$Prodej_60m2_2016 and Bydleni_Brno_60m2_2016$Vzdelani 
    
                  Základní Středoškolské
    Středoškolské 1.00     -            
    Vysokoškolské 0.57     0.89         
    
    P value adjustment method: bonferroni 



    Contrasts


    Umožňují porovnat jednotlivé skupiny v jednom kroku bez nutnosti korigovat hladinu významnosti (bez snížení síly testu)

    • Jen když máme dopředu hypotézy
    • Kontrastů lze provést tolik, kolik je počet skupin – 1

    • Každý kontrast srovnává 2 průměry
    • Průměr skupiny nebo průměr více skupin dohromady
    • Např. „Základní" vs. „Středoškolské"

    • Ortogonální (nezávislé) kontrasty
    • Skupina použitá v jednom srovnání není použitá v dalším

    Neortogonální kontrasty


    • Contrasts:
    c1 = c(-1,0,1)
    c2 = c(0,-1,1)
    
    mat <- cbind(c1,c2)
    contrasts(Bydleni_Brno_60m2_2016$Vzdelani) <- mat
    
    model1 <- lm(Prodej_60m2_2016 ~ Vzdelani, data = Bydleni_Brno_60m2_2016)
    summary(model1)
    
    Call:
    lm(formula = Prodej_60m2_2016 ~ Vzdelani, data = Bydleni_Brno_60m2_2016)
    
    Residuals:
        Min      1Q  Median      3Q     Max 
    -872820 -171177   87618  278193  601098 
    
    Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
    (Intercept)  2807714     100840  27.843   <2e-16 ***
    Vzdelanic1   -178546     158610  -1.126    0.272    
    Vzdelanic2    -25648     117027  -0.219    0.828    
    ---
    Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
    
    Residual standard error: 424100 on 23 degrees of freedom
    Multiple R-squared:  0.07729,   Adjusted R-squared:  -0.002942 
    F-statistic: 0.9633 on 2 and 23 DF,  p-value: 0.3965
    options(contrasts = c("contr.helmert", "contr.poly"))
    
    contrasts(Bydleni_Brno_60m2_2016$Vzdelani) <- "contr.helmert"
    
    model1 <- lm(Prodej_60m2_2016 ~ Vzdelani, data = Bydleni_Brno_60m2_2016)
    
    summary(model1)
    
    Call:
    lm(formula = Prodej_60m2_2016 ~ Vzdelani, data = Bydleni_Brno_60m2_2016)
    
    Residuals:
        Min      1Q  Median      3Q     Max 
    -872820 -171177   87618  278193  601098 
    
    Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
    (Intercept)  2807714     100840  27.843   <2e-16 ***
    Vzdelani1     -76449     117840  -0.649    0.523    
    Vzdelani2    -102097      74430  -1.372    0.183    
    ---
    Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
    
    Residual standard error: 424100 on 23 degrees of freedom
    Multiple R-squared:  0.07729,   Adjusted R-squared:  -0.002942 
    F-statistic: 0.9633 on 2 and 23 DF,  p-value: 0.3965


    Resources

    Conway, A. (n.d.) Intro to Statistics with R: Student’s T-test. Dostupné online na: https://www.datacamp.com/courses/intro-to-statistics-with-rstudents-t-test

    Effect size (n.d.). In Wikipedia: Staženo dne 10. 10. 2016 z https://en.wikipedia.org/wiki/Effect_size

    Field, A., Miles, J., & Field, Z. (2012). Discovering Statistics Using R. Sage: UK.

    Navarro, D. J. (2014). Learning statistics with R: A tutorial for psychology students and other beginners. Available online: http://health.adelaide.edu.au/psychology/ccs/teaching/lsr/

    Standard error (n.d.). In Wikipedia: Staženo dne 10. 10. 2016 z https://en.wikipedia.org/wiki/Standard_error

    Student’s t-test (n.d.). In Wikipedia: Staženo dne 10. 10. 2016 z https://en.wikipedia.org/wiki/Student%27s_ttest

     

    A work by Vit Gabrhel

     

    LS0tCnRpdGxlOiAiKiowOC4gQ29tcGFyaW5nIFR3byBhbmQgTW9yZSBHcm91cHMqKiIKc3VidGl0bGU6ICJSMTAxIgphdXRob3I6ICJWw610IEdhYnJoZWwiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogeWV0aQogICAgY29kZV9mb2xkaW5nOiAic2hvdyIKICAgIAotLS0KCiMjIE91dGxpbmUKPGJyPgoKPHVsPgojIyMjIDAxLiAqKkNvbXBhcmluZyB0d28gZ3JvdXBzICh0LXRlc3RzKSoqCiMjIyMgMDIuICoqQ29tcGFyaW5nIHRocmVlIGFuZCBtb3JlIGdyb3VwcyAoQU5PVkEpKioKPHVsLz4KCjxicj4KCiMjIERhdGEgYW5kIHBhY2thZ2VzCiMjIyAqSG91c2luZyBpbiBCcm5vKgo8YnI+CgoqICoqUGFja2FnZXMqKjoKYGBge3IsIHJlc3VsdD0naGlkZScsIG1lc3NhZ2U9RkFMU0V9CnJlcXVpcmUoInJlYWR4bCIpCnJlcXVpcmUoImRwbHlyIikKcmVxdWlyZSgic3VtbWFyeXRvb2xzIikKcmVxdWlyZSgiZ2dwbG90MiIpCmxpYnJhcnkoInBzeWNoIikKbGlicmFyeShzanN0YXRzKQpsaWJyYXJ5KCJjYXIiKQpgYGAKPGJyPgoKKiAqKkRhdGEqKjoKYGBge3J9CiAgQnlkbGVuaV9Ccm5vIDwtIHJlYWRfZXhjZWwoIkJ5ZGxlbmlfQnJuby54bHN4IikKYGBgCgoKPGJyPgoKIyMgQ29tcGFyaW5nIHR3byBncm91cHMKPGJyPgoKIyMjIERlcGVuZGVudCB0LXRlc3QKPGJyPgoKIVtdKDAxLmpwZykKCjxicj4KCiMjIyMgKkFzc3VtcHRpb25zKgoKPGJyPgoKKiBUaGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiAKICArIEluIHRoZSBkZXBlbmRlbnQgdC10ZXN0IHRoaXMgbWVhbnMgdGhhdCB0aGUgKipzYW1wbGluZyBkaXN0cmlidXRpb24qKiBvZiB0aGUgKipkaWZmZXJlbmNlcyBiZXR3ZWVuIHNjb3JlcyoqIHNob3VsZCBiZSAqKm5vcm1hbCoqLCBub3QgdGhlIHNjb3JlcyB0aGVtc2VsdmVzLgoqIERhdGEgKCoiRGVwZW5kZW50IHZhcmlhYmxlIiopIGFyZSBtZWFzdXJlZCBhdCBsZWFzdCBhdCB0aGUgKippbnRlcnZhbCBsZXZlbCoqLgoKPGJyPgoKIyMjIyAqTWFpbiBQYXJ0IG9mIHRoZSBBbmFseXNpcyoKCjxicj4KCiogSW4gdGhlIGNhc2Ugb2Ygb3VyIGRlcGVuZGVudCB0LXRlc3QsIHdlIG5lZWQgdG8gc3BlY2lmeSB0aGVzZSBhcmd1bWVudHMgdG8gdC50ZXN0KCk6CmBgYHtyLCBldmFsPUZBTFNFfQogP3QudGVzdApgYGAKPGJyPgoKKiAicGFpcmVkPS4uLiI6IFdoZXRoZXIgd2UncmUgZG9pbmcgYSAqKmRlcGVuZGVudCAoaS5lLiBwYWlyZWQpIHQtdGVzdCoqIG9yICoqaW5kZXBlbmRlbnQgdC10ZXN0KiouIAogICAgKyBJbiBjYXNlIG9mIHRoZSAqKnBhaXJlZCB0LXRlc3QqKiBpdCdzICoqVFJVRSoqLgoqIE5vdGUgdGhhdCAqdC50ZXN0KCkqIGNhcnJpZXMgb3V0IGEgKip0d28tc2lkZWQgdC10ZXN0KiogYnkgKipkZWZhdWx0KiouCgo8YnI+CgoqICoqVmFyaWFibGVzKio6CiAgKyB4OiBDb2x1bW4gb2YgKkJ5ZGxlbmlfQnJubyogY29udGFpbmluZyBwcmljZXMgZm9yIDIwMTUuCiAgKyB5OiBDb2x1bW4gb2YgKkJ5ZGxlbmlfQnJubyogY29udGFpbmluZyBwcmljZXMgZm9yIDIwMTYKCjxicj4KCiogKipDb25kdWN0IGEgcGFpcmVkIHQtdGVzdCoqIHVzaW5nIHRoZSB0LnRlc3QgZnVuY3Rpb246CmBgYHtyfQp0LnRlc3QoQnlkbGVuaV9Ccm5vJFByb25hamVtX20yXzIwMTUsIEJ5ZGxlbmlfQnJubyRQcm9uYWplbV9tMl8yMDE2LCBtdSA9IC0xLAogICAgICAgcGFpcmVkID0gVFJVRSkKYGBgCgohW10oMDIuanBnKQoKPGJyPgoKIyMjIyAqRWZmZWN0IFNpemUqCgo8YnI+CgoqIFBhY2thZ2VzOgpgYGB7ciwgcmVzdWx0cz0naGlkZSd9CmxpYnJhcnkoImxzciIpCmxpYnJhcnkoImVmZnNpemUiKQpgYGAKCjxicj4KCiogKipDb2hlbidzIGQqKiwgdGhlICoqZWZmc2l6ZSoqIHdheToKYGBge3IsIGV2YWw9RkFMU0V9CmNvaGVuLmQoeCwgeSwgCiAgICAgICAgcG9vbGVkPVRSVUUsIAogICAgICAgIHBhaXJlZD1UUlVFLAogICAgICAgIG5hLnJtPUZBTFNFLCAKICAgICAgICBoZWRnZXMuY29ycmVjdGlvbj1GQUxTRSwKICAgICAgICBjb25mLmxldmVsPTAuOTUsIAogICAgICAgIG5vbmNlbnRyYWw9RkFMU0UpCgo/Y29oZW4uZCgpCmBgYAoKPGJyPgoKYGBge3J9CmNvaGVuLmQoQnlkbGVuaV9Ccm5vJFByb25hamVtX20yXzIwMTUsIEJ5ZGxlbmlfQnJubyRQcm9uYWplbV9tMl8yMDE2LCBwYWlyZWQ9VFJVRSkKYGBgCgo8YnI+CgojIyMjICpWaXN1YWxpc2F0aW9uIG9mIHRoZSBDb2hlbidzIGQqCgo8YnI+CgoqIFRoaXMgW2FwcF0oaHR0cDovL3NoaW55LnBzeS5nbGEuYWMudWsvZ3Vlc3MvP2ZiY2xpZD1Jd0FSMkVxWGd5U2w3TURvRGVnWWpFODB2STFxRk1hNXFnRXoxSUFlWFJYV3YxMjh4OXkzalFseHNFSEZrKSB3aWxsIHNob3cgeW91IGEgZ3JhcGggb2Ygc2ltdWxhdGVkIGRhdGEgd2l0aCBhIHJhbmRvbSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIGVhY2ggb2YgdHdvIGdyb3VwcyBhbmQgcmFuZG9tIGVmZmVjdCBzaXplLiAKICArIFRoZSBlZmZlY3Qgc2l6ZSB3aWxsIGJlIGJldHdlZW4gLTMgYW5kIDMsIGFuZCB5b3VyIGpvYiBpcyB0byAqKmd1ZXNzIHRoZSBzaXplIG9mIHRoZSBlZmZlY3QqKi4KCjxicj4KICAKIyMjIEluZGVwZW5kZW50IHQtdGVzdAoKPGJyPgoKIVtdKDAzLmpwZykKCjxicj4KCiMjIyMgKkFzc3VtcHRpb25zKgoKPGJyPgoKKiAqKkhvbW9nZW5laXR5IG9mIHZhcmlhbmNlKiouIAoqIFNjb3JlcyBhcmUgKippbmRlcGVuZGVudCoqIChiZWNhdXNlIHRoZXkgY29tZSBmcm9tICpub24taWRlbnRpY2FsIHVuaXRzKikuCiogRGF0YSAoKiJEZXBlbmRlbnQgdmFyaWFibGUiKikgYXJlIG1lYXN1cmVkIGF0IGxlYXN0IGF0IHRoZSAqKmludGVydmFsIGxldmVsKiouCgo8YnI+CgoqIENyZWF0ZSBzdWJzZXRzOgpgYGB7cn0KU2lkbGlzdGUgPC0gQnlkbGVuaV9Ccm5vICU+JQogc2VsZWN0KGMoIlNpZGxpc3RlIiwgIlByb25hamVtX20yXzIwMTYiKSkgJT4lCiBmaWx0ZXIoU2lkbGlzdGUgPT0gMSwgUHJvbmFqZW1fbTJfMjAxNiA+IDApCgpSb2Rpbm5lX0RvbXkgPC0gQnlkbGVuaV9Ccm5vICU+JQogc2VsZWN0KGMoIlNpZGxpc3RlIiwgIlByb25hamVtX20yXzIwMTYiKSkgJT4lCiBmaWx0ZXIoU2lkbGlzdGUgPT0gMCwgUHJvbmFqZW1fbTJfMjAxNiA+IDApCmBgYAoKPGJyPgoKKiBTdW1tYXJ5IHN0YXRpc3RpY3M6CmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIGV2YWwgPSBGQUxTRX0KU2lkbGlzdGVfdmlldyA8LSB2aWV3KGRmU3VtbWFyeShTaWRsaXN0ZSkpCgpSb2Rpbm5lX0RvbXlfdmlldyA8LSB2aWV3KGRmU3VtbWFyeShSb2Rpbm5lX0RvbXkpKQpgYGAKCjxicj4KCiogVmlzdWFsaXNlIHRoZSBkYXRhOgpgYGB7cn0KQnlkbGVuaV9Ccm5vX05hamVtIDwtIEJ5ZGxlbmlfQnJubyAlPiUKIGZpbHRlcihQcm9uYWplbV9tMl8yMDE2ID4gMCkgJT4lCiBnZ3Bsb3QoYWVzKHggPSBmYWN0b3IoU2lkbGlzdGUpLCAKICAgICAgICAgICAgeSA9IFByb25hamVtX20yXzIwMTYsIAogICAgICAgICAgICBmaWxsID0gZmFjdG9yKFNpZGxpc3RlKSkpICsgCiAgZ2VvbV9ib3hwbG90KCkKCkJ5ZGxlbmlfQnJub19OYWplbQpgYGAKCjxicj4KCiMjIyMgTGV2ZW5lJ3MgdGVzdCAoTG9hZCB0aGUgYGNhcmAgcGFja2FnZSk6CmBgYHtyfQojIFdyYW5nbGUgdGhlIGRhdGE6CkJ5ZGxlbmlfQnJub19OYWplbV9MZXZlbmUgPC0gQnlkbGVuaV9Ccm5vICU+JQogZmlsdGVyKFByb25hamVtX20yXzIwMTYgPiAwKQoKIyBMZXZlbmUncyB0ZXN0CmxldmVuZVRlc3QoQnlkbGVuaV9Ccm5vX05hamVtX0xldmVuZSRQcm9uYWplbV9tMl8yMDE2IH4gZmFjdG9yKEJ5ZGxlbmlfQnJub19OYWplbV9MZXZlbmUkU2lkbGlzdGUpKQpgYGAKCjxicj4KCiogQ29uZHVjdCBhbiAqKmluZGVwZW5kZW50IHQtdGVzdCoqOgpgYGB7cn0KICB0LnRlc3QoU2lkbGlzdGUkUHJvbmFqZW1fbTJfMjAxNiwgUm9kaW5uZV9Eb215JFByb25hamVtX20yXzIwMTYsCiAgICAgICAgIHZhci5lcXVhbCA9IEZBTFNFKQpgYGAKCjxicj4KCiMjIyMgKlNpemUgRWZmZWN0KgoKPGJyPgoKIVtdKDA0LmpwZykKCjxicj4KCiogKipDb2hlbidzIGQqKiwgdGhlICoqbHNyKiogd2F5OgoKYGBge3J9CiMgRGF0YQpOYWplbV8yMDE1IDwtIEJ5ZGxlbmlfQnJubyAlPiUgc2VsZWN0KFByb25hamVtX20yXzIwMTUpClNpZGxpc3RlXzIwMTUgPC0gQnlkbGVuaV9Ccm5vICU+JSBzZWxlY3QoU2lkbGlzdGUpCgoKY29oZW5zRChCeWRsZW5pX0Jybm8kUHJvbmFqZW1fbTJfMjAxNSB+IEJ5ZGxlbmlfQnJubyRTaWRsaXN0ZSkKYGBgCgo8YnI+CgoqICoqQ29oZW4ncyBkKiosIHRoZSAqKmVmZnNpemUqKiB3YXk6CmBgYHtyLCBldmFsPVRSVUV9CmNvaGVuLmQoQnlkbGVuaV9Ccm5vJFByb25hamVtX20yXzIwMTUgfiBCeWRsZW5pX0Jybm8kU2lkbGlzdGUpCmBgYAoKIyMgQ29tcGFyaW5nIHRocmVlIGFuZCBtb3JlIGdyb3VwcwoKIyMjIE9uZS13YXkgQU5PVkEKCjxicj4KCiMjIyMgKkludHJvZHVjdGlvbioKCjxicj4KCioqQU5PVkEqKiA9ICoqQU4qKmFseXNpcyAqKk8qKmYgKipWQSoqcmlhbmNlCgo8YnI+CgoqIFVzZWQgd2hlbiAqKmNvbXBhcmluZyBncm91cCBtZWFucyoqIG9mICoqdGhyZWUgYW5kIG1vcmUgZ3JvdXBzL2NvbmRpdGlvbnMqKi4KKiBUd28gZGVmYXVsdCB2YXJpYW50czoKICArICoqQmV0d2VlbiBkZXNpZ24qKjogaW5kZXBlbmRlbnQgc2FtcGxlcyAoQU5PVkEsIEFOQ09WQSwgZmFjdG9yaWFsIEFOT1ZBLCBldGMuKQogICAgKyAqSXMgdGhlcmUgYW55IGRpZmZlcmVuY2UgYmV0d2VlbiByZWdpb25zIHdoZW4gaXQgY29tZXMgdG8gdGhlIGluY29tZSBpbiB0aGUgQ3plY2ggUmVwdWJsaWM/KgoqICoqV2l0aGluIGRlc2lnbioqOiBhIGNvbXBhcmlzb24gb2YgYSBncm91cCBtZWFuIGFjcm9zcyBzZXZlcmFsIHBvaW50cyBpbiB0aW1lIChSZXBlYXRlZCBNZWFzdXJlcyBBTk9WQSkuCiAgKyAqSGF2ZSBob3VzZWhvbGRzIGluY3JlYXNlZCBmdWVsIGNvbnN1bXB0aW9uIGR1cmluZyB0aGUgbGFzdCBmaXZlIHllYXJzPyoKCjxicj4KICAKIyMjIyAqIkdyYW1tYXIiKgoKPGJyPgoKKiAqKkZhY3RvcioqIC0gdGhlIG1vc3QgZnJlcXVlbnQgYXR0YWluZWQgbGV2ZWwgb2YgZWR1Y2F0aW9uIGluIHRoZSByZXNwZWN0aXZlIGNpdHkgZGlzdHJpY3QgKHByaW1hcnksIHNlY29uZGFyeSwgdGVydGlhcnkpCiogKipEZXBlbmRlbnQgdmFyaWFibGUqKiAtIHRoZSBhdmVyYWdlIHByaWNlIGZvciBhbiBhcGFydG1lbnQgKDYwIHNxdWFyZSBtZXRyZXMpIGluIHRoZSByZXNwZWN0aXZlIGNpdHkgZGlzdHJpY3QgaW4gMjAxNi4KKiAqKk51bGwgaHlwb3RoZXNpcyoqOgogICsgKkFsbCBvZiB0aGUgZ3JvdXBzIGhhdmUgc2ltaWxhciBtZWFuLiBBbHRlcm5hdGl2ZWx5LCB0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGluIHRoZSBtZWFuIHZhbHVlIG9mIGFuIGFwYXJ0bWVudCAoNjAgc3F1YXJlIG1ldHJlcykgaW4gQnJubyBiYXNlZCBvbiB0aGUgbW9zdCBmcmVxdWVudCBsZXZlbCBvZiBlZHVjYXRpb24gaW4gdGhlIHJlc3BlY3RpdmUgY2l0eSBkaXN0cmljdC4qCiogKipBbHRlcm5hdGl2ZSBoeXBvdGhlc2lzKio6CiAgKyAqVGhlIGF2ZXJhZ2UgcHJpY2UgZm9yIGEgZmxhdCBvZiA2MCBzcXVhcmUgbWV0cmVzIGlzIHRoZSBoaWdoZXN0IGluIHRoZSBkaXN0cmljdHMgd2l0aCB1bml2ZXJzaXR5IGFsdW1uaSBhcyB0aGUgbW9zdCBmcmVxdWVudCBjYXRlZ29yeSBvZiB0aGUgbGV2ZWwgb2YgZWR1Y2F0aW9uLioKCjxicj4KCiMjIyMgKkYtdGVzdCBhbmQgRi1yYXRpbyoKCiogKipOdWxsIGh5cG90aGVzaXMqKjogKmFsbCBncm91cHMgYXJlIGVxdWFsKgogICsgQU5PVkEgcHJvdmlkZXMgYSAqKnNpZ25pZmljYW5jZSB0ZXN0KioKKiBUZXN0IHN0YXRpc3RpYyBpcyB0aGUgKipGLXRlc3QqKiAob3IgKipGLXJhdGlvKiopCgo8YnI+CgoqICpIb3cgZG8gd2UgZ2V0IHRoZSBhc3NvY2lhdGVkIHAtdmFsdWUqPwogICsgVGhlICJmYW1pbHkiIG9mICoqRi1kaXN0cmlidXRpb25zKiogaXMgYmFzZWQgb246CiAgICArIFRoZSAqKm51bWJlcioqIG9mICoqb2JzZXJ2YXRpb25zKiogKGNhc2VzIG9yIHNpbXBseSAqKm4qKikKICAgICsgVGhlICoqbnVtYmVyKiogb2YgY29tcGFyZWQgY29uZGl0aW9ucyAoKipncm91cHMqKikKCjxicj4KICAgIAojIyMjICpTaW11bGF0aW9uIG9mIHRoZSBGLWRpc3RyaWJ1dGlvbioKCmBgYHtyfQojIENyZWF0ZSB0aGUgdmVjdG9yIHgKeCA8LSBzZXEoZnJvbSA9IDAsIHRvID0gMTAsIGxlbmd0aCA9IDIwMDApCgojIEV2YWx1YXRlIHRoZSBkZW5zaXRpZXMKeV8xIDwtIGRmKHgsIDMsIDEwMCkKeV8yIDwtIGRmKHgsIDEsIDEpCnlfMyA8LSBkZih4LCAyLCAxMDApCnlfNCA8LSBkZih4LCAzLCAzMCkKeV81IDwtIGRmKHgsIDMsIDUwMCkKeV82IDwtIGRmKHgsIDMsIDUwKQp5XzcgPC0gZGYoeCwgNiwgMTAwMCkKCiMgUGxvdCB0aGUgZGVuc2l0aWVzCnBsb3QoeCwgeV8xLCBjb2wgPSAxLCB0eXBlID0gImwiKQpsaW5lcyh4LCB5XzIsIGNvbCA9IDIpCmxpbmVzKHgsIHlfMywgY29sID0gMykKbGluZXMoeCwgeV80LCBjb2wgPSA0KQpsaW5lcyh4LCB5XzUsIGNvbCA9IDUpCmxpbmVzKHgsIHlfNiwgY29sID0gNikKbGluZXMoeCwgeV83LCBjb2wgPSA3KQoKIyBBZGQgdGhlIGxlZ2VuZApsZWdlbmQoInRvcHJpZ2h0IiwgdGl0bGUgPSAiRiBkaXN0cmlidXRpb25zIiwKYygiZGYgPSAoMywgMTAwKSIsICJkZiA9ICgxLCAxKSIsICJkZiA9ICgyLCAxMDApIiwgImRmID0gKDMsIDMwKSIsCiJkZiA9ICgzLCA1MDApIiwgImRmID0gKDMsIDUwKSIsICJkZiA9ICg2LCAxMDAwKSIpLApjb2wgPSBjKDEsIDIsIDMsIDQsIDUsIDYsIDcpLCBsdHkgPSAxKQpgYGAKCjxicj4KCiMjIyMgKlN1bW1hcnkgVGFibGUqCgo8YnI+CgohW10oMDUuanBnKSAKCjxicj4KCiMjIyMgKkNvZGUqCgo8YnI+CgoqIERhdGEgd3JhbmdsaW5nCmBgYHtyfQpCeWRsZW5pX0Jybm8kVnpkZWxhbmkgPSBmYWN0b3IoQnlkbGVuaV9Ccm5vJFZ6ZGVsYW5pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKDEsIDIsIDMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiWsOha2xhZG7DrSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTdMWZZWRvxaFrb2xza8OpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlZ5c29rb8Wha29sc2vDqSIpKQoKQnlkbGVuaV9Ccm5vXzYwbTJfMjAxNiA8LSBCeWRsZW5pX0Jybm8gJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoUHJvZGVqXzYwbTJfMjAxNiA+IDApCmBgYAoKPGJyPgoKKiBTdW1tYXJ5IHN0YXRpc3RpY3MgYnkgZ3JvdXBzOgpgYGB7cn0KZGVzY3JpYmVCeShCeWRsZW5pX0Jybm9fNjBtMl8yMDE2JFByb2Rlal82MG0yXzIwMTYsIAogICAgICAgICAgIGdyb3VwID0gZmFjdG9yKEJ5ZGxlbmlfQnJub182MG0yXzIwMTYkVnpkZWxhbmkpKQpgYGAKCjxicj4KCiogQm94cGxvdDoKYGBge3J9CmJwMSA9IGdncGxvdChCeWRsZW5pX0Jybm9fNjBtMl8yMDE2LCBhZXMoVnpkZWxhbmksIFByb2Rlal82MG0yXzIwMTYpKQpicDEgKyBnZW9tX2JveHBsb3QoYWVzKGZpbGw9VnpkZWxhbmkpLCBhbHBoYT1JKDAuNSkpICsKIGdlb21fcG9pbnQocG9zaXRpb249ImppdHRlciIsIGFscGhhPTAuNSkgKwogZ2VvbV9ib3hwbG90KG91dGxpZXIuc2l6ZT0wLCBhbHBoYT0wLjUpICsKIHRoZW1lKAogYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBjb2xvcj0iYmxhY2siLCBzaXplPTEyKSwKIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIiwgY29sb3I9ImJsYWNrIiwgc2l6ZT0xMiksCiBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBjb2xvciA9ICJibGFjayIsIHNpemU9MTIpKSArCiBsYWJzKHg9IlDFmWV2YcW+dWrDrWPDrSBrYXRlZ29yaWUgdnpkxJtsw6Fuw60gIiwKIHkgPSAiQ2VuYSB6YSBieXQgbyByb3psb3plIDYwIG1ldHLFryDEjXR2ZXJlxI1uw61jaCAodiBLxI0pIiwKIHRpdGxlPSAiQ2VuYSB6YSBieXQgbyByb3psb3plIDYwIG1ldHLFryDEjXR2ZXJlxI1uw61jaCAodiBLxI0pIGRsZSBwxZlldmHFvnVqw61jw60ga2F0ZWdvcmllIHZ6ZMSbbMOhbsOtIikgKwp0aGVtZShsZWdlbmQucG9zaXRpb249J25vbmUnKQoKYGBgCgo8YnI+CgoqIFRoZSAqKmFvdioqIGZ1bmN0aW9uOgpgYGB7ciwgZXZhbD1GQUxTRSwgZXJyb3I9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmFvdihkZXBlbmRlbnRfdmFyIH4gaW5kZXBlbmRlbnRfdmFyKQpzdW1tYXJ5KCkKYGBgCgo8YnI+CgoqIEFwcGx5IHRoZSBhb3YgZnVuY3Rpb246CmBgYHtyfQphbm92YV9CeWRsZW5pQnJubyA8LSBhb3YoUHJvZGVqXzYwbTJfMjAxNiB+IFZ6ZGVsYW5pLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBCeWRsZW5pX0Jybm9fNjBtMl8yMDE2KQpgYGAKCjxicj4KCiogTG9vayBhdCB0aGUgc3VtbWFyeSB0YWJsZSBvZiB0aGUgcmVzdWx0CmBgYHtyfQpzdW1tYXJ5KGFub3ZhX0J5ZGxlbmlCcm5vKQpgYGAKCjxicj4KCiMjIyMgKlNpemUgZWZmZWN0KgoKPGJyPgoKKiBUaGUgKipsc3IqKiBwYWNrYWdlOgpgYGB7cn0KbGlicmFyeShsc3IpCmV0YVNxdWFyZWQoYW5vdmFfQnlkbGVuaUJybm8sIAogICAgICAgICAgIHR5cGUgPSAyLAogICAgICAgICAgIGFub3ZhID0gRkFMU0UpCmBgYAoKPGJyPgoKIVtdKDA2LmpwZykKCjxicj4KCiogVGhlICoqc2pzdGF0cyoqIHBhY2thZ2U6CmBgYHtyfQphbm92YSA8LSBsbShQcm9kZWpfNjBtMl8yMDE2IH4gVnpkZWxhbmksIAogICAgICAgICAgICAgICAgZGF0YSA9IEJ5ZGxlbmlfQnJub182MG0yXzIwMTYpCnBlcmZvcm1hbmNlOjpyMihhbm92YSkKYGBgCgo8YnI+CgohW10oMDcuanBnKQoKPGJyPgoKIVtdKDA4LmpwZykKCjxicj4KCiMjIyMgQXNzdW1wdGlvbnMKCjxicj4KCioqVmFyaWFibGVzKioKCiogKioiRGVwZW5kZW50IiB2YXJpYWJsZSoqIGF0IGxlYXN0IG9uIHRoZSAqKmludGVydmFsIGxldmVsKiouCgo8YnI+CgoqKk5vcm1hbCBkaXN0cmlidXRpb24gb2YgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSoqCgoqIEluICoqZWFjaCoqIG9mIHRoZSAqKmdyb3Vwcy9jb25kaXRpb25zKiouCiogKipWaW9sYXRpb24qKiBvZiB0aGlzIGFzc3VtcHRpb24gKipzaG91bGQgbm90IHN1YnN0YW50aWFsbHkgYmlhcyoqIHRoZSAqKnRlc3QgcmVzdWx0KiogYXMgbG9uZyBhcyB0aGUgKipncm91cHMgaGF2ZSBhIHNpbWlsYXIgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyoqLCBhbmQgaW4gKiplYWNoIGdyb3VwKiosIHRoZXJlIGFyZSAqKmF0IGxlYXN0IDMwIG9ic2VydmF0aW9ucyoqLgoqICoqTm9uLXBhcmFtZXRyaWMqKiBhbHRlcm5hdGl2ZSDigJMgKipLcnVza2FsLVdhbGxpcyB0ZXN0KioKCjxicj4KCioqSG9tb2dlbml0eSBvZiB2YXJpYW5jZSoqCgoqIFNsZWR1amVtZSBMZXZlbsWvdiBGLXRlc3QsIG51bG92w6EgaHlwb3TDqXphIGhvdm/FmcOtIG8gaG9tb2dlbml0xJsgbmFwxZnDrcSNIHNrdXBpbmFtaQoqIFBva3VkIExldmVuxa92IEYtdGVzdCB2eWNow6F6w60gc3RhdGlzdGlja3kgc2lnbmlmaWthbnRuw606CiogU2xlZHVqZW1lIHBvbcSbciByb3pwdHlsdSB1IHNrdXBpbiBzIG5lanbEm3TFocOtbSBhIG5lam1lbsWhw61tIHJvenB0eWxlbSwgcMWZacSNZW3FviBjaGNlbWUsIGFieSBieWwgdGVudG8gcG9txJtyIG1lbsWhw60gbmXFviAzCiogTmFydcWhZW7DrSBieSBuZW3Em2xvIHZhZGl0LCBwb2t1ZCBqc291IHNrdXBpbnkgc3Rlam7EmyB2ZWxrw6kKKiBQxZlpIG5hcnXFoWVuw60gbHplIHBvdcW+w610IFdlbGNob3ZvIEYKCjxicj4KCioqSW5kZXBlbmRlbmNlIG9mIG9ic2VydmF0aW9uKioKCjxicj4KCiMjIyMjICoqSG9tb2dlbml0eSBvZiB2YXJpYW5jZSoqCmBgYHtyfQpsaWJyYXJ5KCJjYXIiKQpgYGAKCjxicj4KCiogSWYgeW91IGRvbid0IHNwZWNpZnkgYWRkaXRpb25hbCBhcmd1bWVudHMsIHRoZSAqKmRldmlhdGlvbiBzY29yZXMqKiBhcmUgY2FsY3VsYXRlZCBieSAqKmNvbXBhcmluZyBlYWNoIHNjb3JlKiogdG8gKippdHMgZ3JvdXAgbWVkaWFuKiouCiAgKyBUaGlzIGlzIHRoZSBkZWZhdWx0IGJlaGF2aW91ciwgZXZlbiB0aG91Z2ggdGhleSBhcmUgdHlwaWNhbGx5IGNhbGN1bGF0ZWQgYnkgY29tcGFyaW5nIGVhY2ggc2NvcmUgdG8gaXRzIGdyb3VwIG1lYW4uCiAgKyBJZiB5b3Ugd2FudCB0byAqKnVzZSBtZWFucyBhbmQgbm90IG1lZGlhbnMqKiwgYWRkIGFuIGFyZ3VtZW50ICoqY2VudGVyID0gbWVhbioqLiBEbyB0aGlzIG5vdyBhbmQgY29tcGFyZSB0aGUgcmVzdWx0cyB0byB0aGUgZmlyc3QgdGVzdC4KCjxicj4KCiogTGV2ZW5lJ3MgdGVzdDoKYGBge3J9CmxldmVuZVRlc3QoUHJvZGVqXzYwbTJfMjAxNiB+IFZ6ZGVsYW5pLCBkYXRhID0gQnlkbGVuaV9Ccm5vXzYwbTJfMjAxNikKYGBgCgo8YnI+CgoqIExldmVuZSdzIHRlc3Qgd2l0aCBjZW50ZXIgPSBtZWFuOgpgYGB7cn0KbGV2ZW5lVGVzdChQcm9kZWpfNjBtMl8yMDE2IH4gVnpkZWxhbmksIGRhdGEgPSBCeWRsZW5pX0Jybm9fNjBtMl8yMDE2LCBjZW50ZXIgPSBtZWFuKQpgYGAKCjxicj4KCiMjIyMjICoqTm9ybWFsaXR5IG9mIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUqKjoKYGBge3J9CmdncGxvdChkYXRhPUJ5ZGxlbmlfQnJub182MG0yXzIwMTYsIGFlcyhQcm9kZWpfNjBtMl8yMDE2KSkgKwogZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAyNTAwMDAsIGNvbD0icmVkIiwKIGFlcyhmaWxsPS4uY291bnQuLikpICsKIHNjYWxlX2ZpbGxfZ3JhZGllbnQoIkNvdW50IiwgbG93ID0gImdyZWVuIiwgaGlnaCA9ICJyZWQiKSArCiBsYWJzKHRpdGxlPSJIaXN0b2dyYW0gY2VueSB6YSBieXQgbyByb3psb3plIDYwIG1ldHLFryDEjXR2ZXJlxI1uw61jaCB2IEJybsSbIHYgcm9jZSAyMDE2IikgKwogbGFicyh4PSJDZW5hIiwgeT0ixIxldG5vc3QiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpCmBgYAoKPGJyPgoKIyMjIyBGLXRlc3QKCiogRmlzY2hlcidzIHRlc3QgKEYtdGVzdCk6CmBgYHtyfQphbm92YV93bV9WTkUgPSBvbmV3YXkudGVzdChQcm9kZWpfNjBtMl8yMDE2IH4gVnpkZWxhbmksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gQnlkbGVuaV9Ccm5vXzYwbTJfMjAxNiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhci5lcXVhbD1UUlVFKQoKYW5vdmFfd21fVk5FCmBgYAoKPGJyPgoKKiBXZWxjaCdzIEYtdGVzdDoKYGBge3J9CmFub3ZhX3dtX1ZFID0gb25ld2F5LnRlc3QoUHJvZGVqXzYwbTJfMjAxNiB+IFZ6ZGVsYW5pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gQnlkbGVuaV9Ccm5vXzYwbTJfMjAxNiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyLmVxdWFsPUZBTFNFKQoKYW5vdmFfd21fVkUKYGBgCgo8YnI+CgojIyMgUG9zdC1Ib2MgVGVzdHMKCjxicj4KCkFsbG93IGZvciBtdWx0aXBsZSBwYWlyd2lzZSBjb21wYXJpc29ucyB3aXRob3V0IGFuIGluY3JlYXNlIGluIHRoZSBwcm9iYWJpbGl0eSBvZiBhIFR5cGUgSSBlcnJvci4KClBvdcW+w612w6FtZSwgcG9rdWQgbmVtw6FtZSBkb3DFmWVkdSBqYXNuw6kgaHlwb3TDqXp5LgoKKiBTcm92bsOhdmFqw60gdsWhZSBzZSB2xaHDrW0g4oCTIGthxb5kb3Ugc2t1cGludSBzIGthxb5kb3UgKGFsZSBuZXVtw60gc2x1xI1vdmF0IHNrdXBpbnkgamFrbyBrb250cmFzdHkpLgogICsgWiBwcmluY2lwdSBqc291IG9ib3VzdHJhbm7DqS4KCiogSmUgamljaCBtbm9obyDigJMgbGnFocOtIHNlIHYgbsSba29saWthIHBhcmFtZXRyZWNoOgogICsgS29uemVydmF0aXZuw60gKENoLiBJSS4gdHlwdSkgdmVyc3VzIExpYmVyw6FsbsOtIChDaC4gSS4gdHlwdSksCiAgKyBNb3N0IGxpYmVyYWwgPSBubyBhZGp1c3RtZW50LAogICsgTW9zdCBjb25zZXJ2YXRpdmUgPSBhZGp1c3QgZm9yIGV2ZXJ5IHBvc3NpYmxlIGNvbXBhcmlzb24gdGhhdCBjb3VsZCBiZSBtYWRlLAogICsgTmUvdmhvZG7DqSBwcm8gcm96ZMOtbG7EmyB2ZWxrw6kgc2t1cGlueSwKICArIE5lL3Zob2Ruw6kgcHJvIHJvemTDrWxuw6kgc2t1cGlub3bDqSByb3pwdHlseS4KCjxicj4KCiMjIyMgPHU+UmVjb21tZW5kYXRpb24gYnkgQS4gRmllbGQ6PC91PgoKPGJyPgoKKiBTdGVqbsSbIHZlbGvDqSBza3VwaW55IGEgc2t1cGlub3bDqSByb3pwdHlseSAoaWRlw6FsbsOtIHNpdHVhY2UpOgogICsgUkVHV1EKICArIFR1a2V5Cgo8YnI+CgoqIFBva3VkIHNpIGNoY2VtZSBiw710IGppc3TDrSwgxb5lIFAgY2h5YnkgSS4gdHlwdSBuZXDFmWVrcm/EjcOtIHp2b2xlbm91IGhsYWRpbnU6CiAgKyBCb25mZXJyb25pCgo8YnI+CgoqIFBva3VkIGpzb3UgdmVsaWtvc3RpIHNrdXBpbiB0cm9jaHUvaG9kbsSbIHJvemTDrWxuw6k6CiAgKyBHYWJyaWVsCiAgKyBIb2NoYmVyZyBHVDIKCjxicj4KIAoqIFBva3VkIHBvY2h5YnVqZW1lIG8gc2hvZG5vc3RpIHNrdXBpbm92w71jaCByb3pwdHlsxa86CiAgKyBHYW1lcy1Ib3dlbGwKICAKPGJyPgogIAojIyMjIyAqKlR1a2V5KioKCjxicj4KCiogQ29uZHVjdCBBTk9WQToKYGBge3J9CmFub3ZhX0J5ZGxlbmlCcm5vID0gYW92KFByb2Rlal82MG0yXzIwMTYgfiBWemRlbGFuaSwgZGF0YSA9IEJ5ZGxlbmlfQnJub182MG0yXzIwMTYpCmBgYAoKPGJyPgoKKiBWaWV3IHN1bW1hcnk6CmBgYHtyfQpzdW1tYXJ5KGFub3ZhX0J5ZGxlbmlCcm5vKQpgYGAKCjxicj4KCiogQ29uZHVjdCBUdWtleSBwcm9jZWR1cmU6CmBgYHtyfQp0dWtleSA8LSBUdWtleUhTRChhbm92YV9CeWRsZW5pQnJubykKYGBgCgo8YnI+CgoqIFBsb3QgY29uZmlkZW5jZSBpbnRlcnZhbHM6CmBgYHtyfQpwbG90KHR1a2V5KQpgYGAKCjxicj4KCiMjIyMgKipCb25mZXJyb25pKioKCjxicj4KClRoZSBCb25mZXJyb25pIGNvcnJlY3Rpb24gY29tcGVuc2F0ZXMgZm9yIHRoYXQgaW5jcmVhc2UgYnkgdGVzdGluZyBlYWNoIGluZGl2aWR1YWwgaHlwb3RoZXNpcyBhdCBhIHNpZ25pZmljYW5jZSBsZXZlbCBvZiDOsS9tLCB3aGVyZSDOsSBpcyB0aGUgZGVzaXJlZCBvdmVyYWxsIGFscGhhIGxldmVsIGFuZCBtIGlzIHRoZSBudW1iZXIgb2YgaHlwb3RoZXNlcy4KCiogRm9yIGV4YW1wbGUsIGlmIGEgdHJpYWwgaXMgdGVzdGluZyBtID0gMjAgaHlwb3RoZXNlcyB3aXRoIGEgZGVzaXJlZCDOsSA9IDAuMDUsIHRoZW4gdGhlIEJvbmZlcnJvbmkgY29ycmVjdGlvbiB3b3VsZCB0ZXN0IGVhY2ggaW5kaXZpZHVhbCBoeXBvdGhlc2lzIGF0IM6xID0gMC4wNS8yMCA9IDAuMDAyNS4KCjxicj4KCiogUGFpcndpc2UgdC10ZXN0OgpgYGB7cn0KcGFpcndpc2UudC50ZXN0KEJ5ZGxlbmlfQnJub182MG0yXzIwMTYkUHJvZGVqXzYwbTJfMjAxNiwKQnlkbGVuaV9Ccm5vXzYwbTJfMjAxNiRWemRlbGFuaSwgcC5hZGp1c3QgPSAiYm9uZmVycm9uaSIpCmBgYAoKPGJyPgoKIVtdKDA5LmpwZykKCjxicj4KCiMjIyBDb250cmFzdHMKCjxicj4KClVtb8W+xYh1asOtICoqcG9yb3ZuYXQgamVkbm90bGl2w6kgc2t1cGlueSoqIHYgKipqZWRub20ga3Jva3UqKiBiZXogbnV0bm9zdGkga29yaWdvdmF0IGhsYWRpbnUgdsO9em5hbW5vc3RpICgqKmJleiBzbsOtxb5lbsOtIHPDrWx5IHRlc3R1KiopCgoqIEplbiBrZHnFviBtw6FtZSBkb3DFmWVkdSBoeXBvdMOpenkKKiBLb250cmFzdMWvIGx6ZSBwcm92w6lzdCB0b2xpaywga29saWsgamUgcG/EjWV0IHNrdXBpbiDigJMgMSAKCiogKipLYcW+ZMO9IGtvbnRyYXN0IHNyb3Zuw6F2w6EgMiBwcsWvbcSbcnkqKgoqIFByxa9txJtyIHNrdXBpbnkgbmVibyBwcsWvbcSbciB2w61jZSBza3VwaW4gZG9ocm9tYWR5CiogTmFwxZkuIOKAnlrDoWtsYWRuw60iIHZzLiDigJ5TdMWZZWRvxaFrb2xza8OpIgoKKiAqKk9ydG9nb27DoWxuw60gKG5lesOhdmlzbMOpKSBrb250cmFzdHkqKgoqIFNrdXBpbmEgcG91xb5pdMOhIHYgamVkbm9tIHNyb3Zuw6Fuw60gbmVuw60gcG91xb5pdMOhIHYgZGFsxaHDrW0KCioqTmVvcnRvZ29uw6FsbsOtIGtvbnRyYXN0eSoqCgo8YnI+CgoqIENvbnRyYXN0czoKYGBge3J9CmMxID0gYygtMSwwLDEpCmMyID0gYygwLC0xLDEpCgptYXQgPC0gY2JpbmQoYzEsYzIpCmNvbnRyYXN0cyhCeWRsZW5pX0Jybm9fNjBtMl8yMDE2JFZ6ZGVsYW5pKSA8LSBtYXQKCm1vZGVsMSA8LSBsbShQcm9kZWpfNjBtMl8yMDE2IH4gVnpkZWxhbmksIGRhdGEgPSBCeWRsZW5pX0Jybm9fNjBtMl8yMDE2KQpzdW1tYXJ5KG1vZGVsMSkKCm9wdGlvbnMoY29udHJhc3RzID0gYygiY29udHIuaGVsbWVydCIsICJjb250ci5wb2x5IikpCgpjb250cmFzdHMoQnlkbGVuaV9Ccm5vXzYwbTJfMjAxNiRWemRlbGFuaSkgPC0gImNvbnRyLmhlbG1lcnQiCgptb2RlbDEgPC0gbG0oUHJvZGVqXzYwbTJfMjAxNiB+IFZ6ZGVsYW5pLCBkYXRhID0gQnlkbGVuaV9Ccm5vXzYwbTJfMjAxNikKCnN1bW1hcnkobW9kZWwxKQpgYGAKCjxicj4KCiMjIFJlc291cmNlcwoKQ29ud2F5LCBBLiAobi5kLikgSW50cm8gdG8gU3RhdGlzdGljcyB3aXRoIFI6IFN0dWRlbnQncyBULXRlc3QuIERvc3R1cG7DqSBvbmxpbmUgbmE6Cmh0dHBzOi8vd3d3LmRhdGFjYW1wLmNvbS9jb3Vyc2VzL2ludHJvLXRvLXN0YXRpc3RpY3Mtd2l0aC1yc3R1ZGVudHMtdC10ZXN0CgpFZmZlY3Qgc2l6ZSAobi5kLikuIEluIFdpa2lwZWRpYTogU3Rhxb5lbm8gZG5lIDEwLiAxMC4gMjAxNiB6IGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0VmZmVjdF9zaXplCgpGaWVsZCwgQS4sIE1pbGVzLCBKLiwgJiBGaWVsZCwgWi4gKDIwMTIpLiBEaXNjb3ZlcmluZyBTdGF0aXN0aWNzIFVzaW5nIFIuIFNhZ2U6IFVLLgoKTmF2YXJybywgRC4gSi4gKDIwMTQpLiBMZWFybmluZyBzdGF0aXN0aWNzIHdpdGggUjogQSB0dXRvcmlhbCBmb3IgcHN5Y2hvbG9neSBzdHVkZW50cyBhbmQgb3RoZXIgYmVnaW5uZXJzLiBBdmFpbGFibGUKb25saW5lOiBodHRwOi8vaGVhbHRoLmFkZWxhaWRlLmVkdS5hdS9wc3ljaG9sb2d5L2Njcy90ZWFjaGluZy9sc3IvCgpTdGFuZGFyZCBlcnJvciAobi5kLikuIEluIFdpa2lwZWRpYTogU3Rhxb5lbm8gZG5lIDEwLiAxMC4gMjAxNiB6IGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1N0YW5kYXJkX2Vycm9yCgpTdHVkZW50J3MgdC10ZXN0IChuLmQuKS4gSW4gV2lraXBlZGlhOiBTdGHFvmVubyBkbmUgMTAuIDEwLiAyMDE2IHogaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU3R1ZGVudCUyN3NfdHRlc3QKCiZuYnNwOwo8aHIgLz4KPHAgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPkEgd29yayBieSA8YSBocmVmPSJodHRwczovL2dpdGh1Yi5jb20vVkdhYnJoZWwiPlZpdCBHYWJyaGVsPC9hPjwvcD4KPHAgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIHN0eWxlPSJjb2xvcjogIzgwODA4MDsiPjxlbT52aXQuZ2FicmhlbEBnbWFpbC5jb208L2VtPjwvc3Bhbj48L3A+Cgo8IS0tIEFkZCBpY29uIGxpYnJhcnkgLS0+CjxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvZm9udC1hd2Vzb21lLzQuNy4wL2Nzcy9mb250LWF3ZXNvbWUubWluLmNzcyI+Cgo8IS0tIEFkZCBmb250IGF3ZXNvbWUgaWNvbnMgLS0+CjxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXI7Ij4KICAgIDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9WR2FicmhlbCIgY2xhc3M9ImZhIGZhLWdpdGh1YiI+PC9hPgogICAgPGEgaHJlZj0iaHR0cHM6Ly9jei5saW5rZWRpbi5jb20vaW4vdiVDMyVBRHQtZ2FicmhlbC0yYjBhOGI5OCIgY2xhc3M9ImZhIGZhLWxpbmtlZGluIj48L2E+CiAgICA8YSBocmVmPSJodHRwczovL3NjaG9sYXIuZ29vZ2xlLmNvbS9jaXRhdGlvbnM/dXNlcj1ZLU5HSmVrQUFBQUomaGw9ZW4mb2k9YW8iIGNsYXNzPSJmYSBmYS1ib29rIj48L2E+CjwvcD4KCiZuYnNwOwoKCiAg