What is this

This is a tutorial document to use R for creating an analysis report. It includes the following elements:

  • Data transformation.
  • Data visualization.
  • Using the gender package to infer the gender based on first name.
  • Using, interpreting, and evaluating binomial logistic regression results.

Aim

We will use the Bechdel test dataset from the TidyTuesday project to predict if a movie passed the test or not, based on the gender of the writers. Dataset and info available from here: https://github.com/rfordatascience/tidytuesday/blob/master/data/2021/2021-03-09/readme.md This dataset is collected by https://fivethirtyeight.com/

What is the Bechdel test?

According to Wikipedia:

The Bechdel test (/ˈbɛkdəl/ BEK-dəl), also known as the Bechdel–Wallace test, is a measure of the representation of women in fiction. It asks whether a work features at least two women who talk to each other about something other than a man.

There are two datasets in the project. The first one contains 8839 Bechdel ratings, movie title, and release year.

The second dataset contains more information about the movies, but contains movies only from 1970 to 2013. Even though we could harvest the movie data from imdb for the first dataset, we will settle with using the second dataset, containing 1794 movies. Interested readers can improve this analysis by extending the analysis to more movies using the imdb API, or a related R package.

Our aim is to predict if a movie is passing the Bechdel test. Our predictor is the proportion of women in the writer team, the year of release, and the runtime. The latter will be used as a control variable, I assume that longer movies might have more chance to introduce more complex female characters.

Preprocessing the data

Identifying the gender of writers

The dataset contains the names of the writers for each movie. Using this, we first get a list of first names for each movie. Then we will use the {gender} package to assume the gender associated with each unique name.

The gender package assumes the gender, based on the first name. As different names are used for both males and females in different times, it also requires a year as an input. Then it checks the proportion of females vs. males that had that name in the specific year, and assumes a gender.

In my analysis, I will make an arbitrary decision to use 1970 as the year for the classification (a sensitivity analysis could be added to see if this introduces much error, I guess not).

# First get all unique first names from writer teams
writer_names <- 
    raw_movies %>% 
    separate_rows(writer, sep = ", ") %>% 
    transmute(writer_name = str_match(writer, pattern = "(^\\w+) .*")[,2])

# Use the gender package to assume a gender in 1970
name_gender <- 
    gender(unique(writer_names$writer_name), year = 1970) %>% 
    select(writer_name = name, 
           gender)

Then I calculate the proportion of females in each writer team for each movie.

# Calculate the proportion of female writers for each movie
female_writers <- 
    raw_movies %>% 
    separate_rows(writer, sep = ", ") %>%
    mutate(writer_name = str_match(writer, pattern = "(^\\w+) .*")[,2]) %>% 
    left_join(name_gender, by = "writer_name") %>% 
    group_by(imdb_id) %>% 
    summarise(female_writer = mean(gender == "female", na.rm = TRUE)) %>% 
    drop_na()

Finally, I create the final analysis dataset, where I only keep relevant variables, recode the outcome to numeric (0/1), create a decade variable instead of year, and convert the runtime in hours.

movies <-
    raw_movies %>% 
    transmute(imdb_id,
              title,
              bechdel_pass = recode(binary, "FAIL" = 0L, "PASS" = 1L),
              decade = (year %/% 10)*10,
              runtime = parse_number(runtime)/60,
              writer) %>% 
    left_join(female_writers, by = "imdb_id") %>% 
    drop_na(female_writer)

Creating a model

I will use the the proportion of female writers as a main predictor, but as control variables, I will also add the decade of release and the runtime in minutes. A binomial logistic model will be fit to predict the passing on the Bechdel test.

bechdel_model <-
    glm(bechdel_pass ~ female_writer + decade + runtime, 
        data = movies, 
        family = "binomial")

# This is the standard regression output
summary(bechdel_model)
## 
## Call:
## glm(formula = bechdel_pass ~ female_writer + decade + runtime, 
##     family = "binomial", data = movies)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.0058  -0.9919  -0.8589   1.3122   1.8802  
## 
## Coefficients:
##                 Estimate Std. Error z value Pr(>|z|)    
## (Intercept)   -28.091190  11.059813  -2.540  0.01109 *  
## female_writer   2.107092   0.215895   9.760  < 2e-16 ***
## decade          0.014191   0.005525   2.568  0.01022 *  
## runtime        -0.433736   0.161521  -2.685  0.00725 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 2118.3  on 1545  degrees of freedom
## Residual deviance: 1980.2  on 1542  degrees of freedom
##   (1 observation deleted due to missingness)
## AIC: 1988.2
## 
## Number of Fisher Scoring iterations: 4
# This is the tidy approach, that converts log likelihoods to odds ratios, and adds confidence intervals.
tidy(bechdel_model, conf.int = TRUE, exponentiate = TRUE)
## # A tibble: 4 x 7
##   term          estimate std.error statistic  p.value conf.low conf.high
##   <chr>            <dbl>     <dbl>     <dbl>    <dbl>    <dbl>     <dbl>
## 1 (Intercept)   6.31e-13  11.1         -2.54 1.11e- 2 2.08e-22   0.00145
## 2 female_writer 8.22e+ 0   0.216        9.76 1.68e-22 5.44e+ 0  12.7    
## 3 decade        1.01e+ 0   0.00553      2.57 1.02e- 2 1.00e+ 0   1.03   
## 4 runtime       6.48e- 1   0.162       -2.69 7.25e- 3 4.71e- 1   0.887
# This is for conveniently making an APA compatible summary table. We also use robust statndard errors to account for the heteroscedasticity.

tab_model(bechdel_model, 
          show.aic = TRUE,
          show.loglik = TRUE, 
          string.ci = "95% CI", 
          show.stat = TRUE,
          robust = TRUE,
          dv.labels = "Passing the Bechdel test",
          pred.labels = c("(Intercept)",
                          "% female in writer team", 
                          "Decade of release", 
                          "Runtime (h)"))
  Passing the Bechdel test
Predictors Odds Ratios 95% CI Statistic p
(Intercept) 0.00 0.00 – 0.00 -2.58 0.010
% female in writer team 8.22 5.43 – 12.46 9.94 <0.001
Decade of release 1.01 1.00 – 1.03 2.60 0.009
Runtime (h) 0.65 0.47 – 0.89 -2.71 0.007
Observations 1546
R2 Tjur 0.086
AIC 1988.156
log-Likelihood -990.078

The results reveal that all predictors are significant. This means that the chance of passing the Bechdel test is 8.22-fold if the entire writer team is female, compared to an all male writer team. This holds even if we account for the effects of the release decade (newer movies pass the test more often), and runtime (longer movies are more likely to fail the test, wtf?).

Assumption checks are available from the {performance} package, that is part of the {easystats} ecosystem. I won’t really do much about the assumption checks now, other than showing the output.

check_model(bechdel_model)

Evaluate model performance

In general, 44% of the investigated movies passed the Bechdel test. Full male teams are twice as likely to fail the test, while full female writer teams are approximately 4 times more likely to pass the test. Thus there is an 8-fold difference between all male and all female writer teams.

# Create a prediction dataset with all possible predictor values
newdata <-
    crossing(female_writer = seq(0,1,.025),
             decade = seq(1970, 2010, 10),
             runtime = unique(movies$runtime))

# Interpolate the predicted values, and create a
augment(bechdel_model, newdata = newdata, type.predict = "link") %>%
    group_by(female_writer) %>% 
    mutate( .fitted = exp(.fitted),
            avg_fitted = mean(.fitted, na.rm = TRUE)) %>% 
    ungroup() %>% 
    ggplot() +
    aes(x = female_writer, y = .fitted) +
    geom_hline(yintercept = 1, color = "red", lty = "dashed") +
    geom_point(alpha = .01) +
    geom_line(aes(y = avg_fitted), color = "blue", size = 2, alpha = .5) +
    scale_x_continuous(labels = scales::percent_format()) +
    labs(x = "% of female writers for the movie",
         y = "Odds ratio of passing the Bechdel test",
         title = "Proportion of female writers vs. passing the Bechdel test in movies",
         subtitle = "When all writers are female, passing the Bechdel test is 8 times more likely compared to an all male writer team.")

ROC curve

Finally, we will create a ROC curve and calculate its AUC to see how efficiently our model can predict the outcome, based on the predictors.

mod_pred <-
    augment(bechdel_model, type.predict = "response") %>% 
    transmute(truth = as.factor(bechdel_pass), 
              estimate = .fitted)

The AUC for the model is ok, but not great: 64%.

roc_bechdel <- roc_curve(mod_pred, truth, estimate, event_level = "second")
    
autoplot(roc_bechdel) +
    scale_x_continuous(labels = percent_format()) +
    scale_y_continuous(labels = percent_format()) +
    labs(title = "ROC curve for the model")

Improvement ideas

  • It would be possible to create a more comprehensive proportion of females variable, taking into account the director(s) along with the writers.
  • Gender classification could be improved by tinkering with the parameters of the gender package.
  • Other types of models could be more useful, e.g. random forest.
  • The words from the description of the movie could be added as predictors.
LS0tDQp0aXRsZTogIlBhc3NpbmcgdGhlIEJlY2hkZWwgdGVzdCBpbiBtb3ZpZXMiDQphdXRob3I6ICJUYW1hcyBOYWd5Ig0KZGF0ZTogIjUvMjYvMjAyMSINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICB0aGVtZTogc3BhY2VsYWINCiAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgIHRvYzogdHJ1ZQ0KICAgdG9jX2Zsb2F0OiB0cnVlDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZSA9IEZBTFNFKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSBGQUxTRSkNCg0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGdlbmRlcikNCmxpYnJhcnkoYnJvb20pDQpsaWJyYXJ5KHNqUGxvdCkNCmxpYnJhcnkoZWFzeXN0YXRzKQ0KbGlicmFyeShzY2FsZXMpDQpsaWJyYXJ5KHlhcmRzdGljaykNCg0KdGhlbWVfc2V0KHRoZW1lX2xpZ2h0KCkpDQoNCmBgYA0KDQoNCiMgV2hhdCBpcyB0aGlzDQoNClRoaXMgaXMgYSB0dXRvcmlhbCBkb2N1bWVudCB0byB1c2UgUiBmb3IgY3JlYXRpbmcgYW4gYW5hbHlzaXMgcmVwb3J0LiBJdCBpbmNsdWRlcyB0aGUgZm9sbG93aW5nIGVsZW1lbnRzOg0KDQotIERhdGEgdHJhbnNmb3JtYXRpb24uDQotIERhdGEgdmlzdWFsaXphdGlvbi4NCi0gVXNpbmcgdGhlIGdlbmRlciBwYWNrYWdlIHRvIGluZmVyIHRoZSBnZW5kZXIgYmFzZWQgb24gZmlyc3QgbmFtZS4NCi0gVXNpbmcsIGludGVycHJldGluZywgYW5kIGV2YWx1YXRpbmcgYmlub21pYWwgbG9naXN0aWMgcmVncmVzc2lvbiByZXN1bHRzLg0KDQojIEFpbQ0KDQpXZSB3aWxsIHVzZSB0aGUgQmVjaGRlbCB0ZXN0IGRhdGFzZXQgZnJvbSB0aGUgVGlkeVR1ZXNkYXkgcHJvamVjdCB0byBwcmVkaWN0IGlmIGEgbW92aWUgcGFzc2VkIHRoZSB0ZXN0IG9yIG5vdCwgYmFzZWQgb24gdGhlIGdlbmRlciBvZiB0aGUgd3JpdGVycy4gRGF0YXNldCBhbmQgaW5mbyBhdmFpbGFibGUgIGZyb20gaGVyZTogaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9ibG9iL21hc3Rlci9kYXRhLzIwMjEvMjAyMS0wMy0wOS9yZWFkbWUubWQNClRoaXMgZGF0YXNldCBpcyBjb2xsZWN0ZWQgYnkgaHR0cHM6Ly9maXZldGhpcnR5ZWlnaHQuY29tLyANCg0KIyMgV2hhdCBpcyB0aGUgQmVjaGRlbCB0ZXN0Pw0KQWNjb3JkaW5nIHRvIFdpa2lwZWRpYToNCg0KPiBUaGUgQmVjaGRlbCB0ZXN0ICgvy4hiyZtrZMmZbC8gQkVLLWTJmWwpLCBhbHNvIGtub3duIGFzIHRoZSBCZWNoZGVs4oCTV2FsbGFjZSB0ZXN0LCBpcyBhIG1lYXN1cmUgb2YgdGhlIHJlcHJlc2VudGF0aW9uIG9mIHdvbWVuIGluIGZpY3Rpb24uIEl0IGFza3Mgd2hldGhlciBhIHdvcmsgZmVhdHVyZXMgYXQgbGVhc3QgdHdvIHdvbWVuIHdobyB0YWxrIHRvIGVhY2ggb3RoZXIgYWJvdXQgc29tZXRoaW5nIG90aGVyIHRoYW4gYSBtYW4uIA0KDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQojIFJlYWQgdGhlIGRhdGENCg0KcmF3X2JlY2hkZWwgPC0gcmVhZHI6OnJlYWRfY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMjEvMjAyMS0wMy0wOS9yYXdfYmVjaGRlbC5jc3YnKQ0KDQpyYXdfbW92aWVzIDwtIHJlYWRfY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMjEvMjAyMS0wMy0wOS9tb3ZpZXMuY3N2JywNCiAgICAgICAgICAgICAgICAgICAgICAgIyBUaGUgZGF0YXNldCBoYXMgZGlmZmVyZW50IHdheXMgdG8gbWFyayBtaXNzaW5nIGRhdGENCiAgICAgICAgICAgICAgICAgICAgICAgbmEgPSBjKCJOQSIsICJOL0EiLCAiI04vQSIpKQ0KDQpgYGANCg0KVGhlcmUgYXJlIHR3byBkYXRhc2V0cyBpbiB0aGUgcHJvamVjdC4gVGhlIGZpcnN0IG9uZSBjb250YWlucyBgciBucm93KHJhd19iZWNoZGVsKWAgQmVjaGRlbCByYXRpbmdzLCBtb3ZpZSB0aXRsZSwgYW5kIHJlbGVhc2UgeWVhci4gDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQojIExldCdzIGp1c3QgbWFrZSB0d28gcXVpY2sgcGxvdHMgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHRlc3QgcmVzdWx0cyBhbmQgdGhlIGRpc3RyaWJ1dGlvbiBieSB5ZWFyLg0KcmF3X2JlY2hkZWwgJT4lIA0KICAgIGdncGxvdCgpICsNCiAgICBhZXMoeCA9IHJhdGluZykgKw0KICAgIGdlb21faGlzdG9ncmFtKCkNCg0KcmF3X2JlY2hkZWwgJT4lIA0KICAgIGdncGxvdCgpICsNCiAgICBhZXMoeCA9IHllYXIsIHkgPSByYXRpbmcsIGdyb3VwID0gcmF0aW5nLCBmaWxsID0gYXMuZmFjdG9yKHJhdGluZykpICsNCiAgICBnZW9tX3Zpb2xpbihzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogICAgZ2VvbV9wb2ludChhbHBoYSA9IC4wMSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkNCg0KYGBgDQoNClRoZSBzZWNvbmQgZGF0YXNldCBjb250YWlucyBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBtb3ZpZXMsIGJ1dCBjb250YWlucyBtb3ZpZXMgb25seSBmcm9tIDE5NzAgdG8gMjAxMy4gRXZlbiB0aG91Z2ggd2UgY291bGQgaGFydmVzdCB0aGUgbW92aWUgZGF0YSBmcm9tIGltZGIgZm9yIHRoZSBmaXJzdCBkYXRhc2V0LCB3ZSB3aWxsIHNldHRsZSB3aXRoIHVzaW5nIHRoZSBzZWNvbmQgZGF0YXNldCwgY29udGFpbmluZyBgciBucm93KHJhd19tb3ZpZXMpYCBtb3ZpZXMuIEludGVyZXN0ZWQgcmVhZGVycyBjYW4gaW1wcm92ZSB0aGlzIGFuYWx5c2lzIGJ5IGV4dGVuZGluZyB0aGUgYW5hbHlzaXMgdG8gbW9yZSBtb3ZpZXMgdXNpbmcgdGhlIGltZGIgQVBJLCBvciBhIHJlbGF0ZWQgUiBwYWNrYWdlLg0KDQpPdXIgYWltIGlzIHRvIHByZWRpY3QgaWYgYSBtb3ZpZSBpcyBwYXNzaW5nIHRoZSBCZWNoZGVsIHRlc3QuIE91ciBwcmVkaWN0b3IgaXMgdGhlIHByb3BvcnRpb24gb2Ygd29tZW4gaW4gdGhlIHdyaXRlciB0ZWFtLCB0aGUgeWVhciBvZiByZWxlYXNlLCBhbmQgdGhlIHJ1bnRpbWUuIFRoZSBsYXR0ZXIgd2lsbCBiZSB1c2VkIGFzIGEgY29udHJvbCB2YXJpYWJsZSwgSSBhc3N1bWUgdGhhdCBsb25nZXIgbW92aWVzIG1pZ2h0IGhhdmUgbW9yZSBjaGFuY2UgdG8gaW50cm9kdWNlIG1vcmUgY29tcGxleCBmZW1hbGUgY2hhcmFjdGVycy4NCg0KIyMgUHJlcHJvY2Vzc2luZyB0aGUgZGF0YQ0KDQojIyMgSWRlbnRpZnlpbmcgdGhlIGdlbmRlciBvZiB3cml0ZXJzDQoNClRoZSBkYXRhc2V0IGNvbnRhaW5zIHRoZSBuYW1lcyBvZiB0aGUgd3JpdGVycyBmb3IgZWFjaCBtb3ZpZS4gVXNpbmcgdGhpcywgd2UgZmlyc3QgZ2V0IGEgbGlzdCBvZiBmaXJzdCBuYW1lcyBmb3IgZWFjaCBtb3ZpZS4gVGhlbiB3ZSB3aWxsIHVzZSB0aGUgYHtnZW5kZXJ9YCBwYWNrYWdlIHRvIGFzc3VtZSB0aGUgZ2VuZGVyIGFzc29jaWF0ZWQgd2l0aCBlYWNoIHVuaXF1ZSBuYW1lLg0KDQpUaGUgZ2VuZGVyIHBhY2thZ2UgYXNzdW1lcyB0aGUgZ2VuZGVyLCBiYXNlZCBvbiB0aGUgZmlyc3QgbmFtZS4gQXMgZGlmZmVyZW50IG5hbWVzIGFyZSB1c2VkIGZvciBib3RoIG1hbGVzIGFuZCBmZW1hbGVzIGluIGRpZmZlcmVudCB0aW1lcywgaXQgYWxzbyByZXF1aXJlcyBhIHllYXIgYXMgYW4gaW5wdXQuIFRoZW4gaXQgY2hlY2tzIHRoZSBwcm9wb3J0aW9uIG9mIGZlbWFsZXMgdnMuIG1hbGVzIHRoYXQgaGFkIHRoYXQgbmFtZSBpbiB0aGUgc3BlY2lmaWMgeWVhciwgYW5kIGFzc3VtZXMgYSBnZW5kZXIuDQoNCkluIG15IGFuYWx5c2lzLCBJIHdpbGwgbWFrZSBhbiBhcmJpdHJhcnkgZGVjaXNpb24gdG8gdXNlIDE5NzAgYXMgdGhlIHllYXIgZm9yIHRoZSBjbGFzc2lmaWNhdGlvbiAoYSBzZW5zaXRpdml0eSBhbmFseXNpcyBjb3VsZCBiZSBhZGRlZCB0byBzZWUgaWYgdGhpcyBpbnRyb2R1Y2VzIG11Y2ggZXJyb3IsIEkgZ3Vlc3Mgbm90KS4NCg0KDQpgYGB7cn0NCiMgRmlyc3QgZ2V0IGFsbCB1bmlxdWUgZmlyc3QgbmFtZXMgZnJvbSB3cml0ZXIgdGVhbXMNCndyaXRlcl9uYW1lcyA8LSANCiAgICByYXdfbW92aWVzICU+JSANCiAgICBzZXBhcmF0ZV9yb3dzKHdyaXRlciwgc2VwID0gIiwgIikgJT4lIA0KICAgIHRyYW5zbXV0ZSh3cml0ZXJfbmFtZSA9IHN0cl9tYXRjaCh3cml0ZXIsIHBhdHRlcm4gPSAiKF5cXHcrKSAuKiIpWywyXSkNCg0KIyBVc2UgdGhlIGdlbmRlciBwYWNrYWdlIHRvIGFzc3VtZSBhIGdlbmRlciBpbiAxOTcwDQpuYW1lX2dlbmRlciA8LSANCiAgICBnZW5kZXIodW5pcXVlKHdyaXRlcl9uYW1lcyR3cml0ZXJfbmFtZSksIHllYXIgPSAxOTcwKSAlPiUgDQogICAgc2VsZWN0KHdyaXRlcl9uYW1lID0gbmFtZSwgDQogICAgICAgICAgIGdlbmRlcikNCg0KYGBgDQoNClRoZW4gSSBjYWxjdWxhdGUgdGhlIHByb3BvcnRpb24gb2YgZmVtYWxlcyBpbiBlYWNoIHdyaXRlciB0ZWFtIGZvciBlYWNoIG1vdmllLiANCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbiBvZiBmZW1hbGUgd3JpdGVycyBmb3IgZWFjaCBtb3ZpZQ0KZmVtYWxlX3dyaXRlcnMgPC0gDQogICAgcmF3X21vdmllcyAlPiUgDQogICAgc2VwYXJhdGVfcm93cyh3cml0ZXIsIHNlcCA9ICIsICIpICU+JQ0KICAgIG11dGF0ZSh3cml0ZXJfbmFtZSA9IHN0cl9tYXRjaCh3cml0ZXIsIHBhdHRlcm4gPSAiKF5cXHcrKSAuKiIpWywyXSkgJT4lIA0KICAgIGxlZnRfam9pbihuYW1lX2dlbmRlciwgYnkgPSAid3JpdGVyX25hbWUiKSAlPiUgDQogICAgZ3JvdXBfYnkoaW1kYl9pZCkgJT4lIA0KICAgIHN1bW1hcmlzZShmZW1hbGVfd3JpdGVyID0gbWVhbihnZW5kZXIgPT0gImZlbWFsZSIsIG5hLnJtID0gVFJVRSkpICU+JSANCiAgICBkcm9wX25hKCkNCmBgYA0KDQpGaW5hbGx5LCBJIGNyZWF0ZSB0aGUgZmluYWwgYW5hbHlzaXMgZGF0YXNldCwgd2hlcmUgSSBvbmx5IGtlZXAgcmVsZXZhbnQgdmFyaWFibGVzLCByZWNvZGUgdGhlIG91dGNvbWUgdG8gbnVtZXJpYyAoMC8xKSwgY3JlYXRlIGEgZGVjYWRlIHZhcmlhYmxlIGluc3RlYWQgb2YgeWVhciwgYW5kIGNvbnZlcnQgdGhlIHJ1bnRpbWUgaW4gaG91cnMuDQoNCmBgYHtyfQ0KbW92aWVzIDwtDQogICAgcmF3X21vdmllcyAlPiUgDQogICAgdHJhbnNtdXRlKGltZGJfaWQsDQogICAgICAgICAgICAgIHRpdGxlLA0KICAgICAgICAgICAgICBiZWNoZGVsX3Bhc3MgPSByZWNvZGUoYmluYXJ5LCAiRkFJTCIgPSAwTCwgIlBBU1MiID0gMUwpLA0KICAgICAgICAgICAgICBkZWNhZGUgPSAoeWVhciAlLyUgMTApKjEwLA0KICAgICAgICAgICAgICBydW50aW1lID0gcGFyc2VfbnVtYmVyKHJ1bnRpbWUpLzYwLA0KICAgICAgICAgICAgICB3cml0ZXIpICU+JSANCiAgICBsZWZ0X2pvaW4oZmVtYWxlX3dyaXRlcnMsIGJ5ID0gImltZGJfaWQiKSAlPiUgDQogICAgZHJvcF9uYShmZW1hbGVfd3JpdGVyKQ0KDQpgYGANCg0KIyMgQ3JlYXRpbmcgYSBtb2RlbA0KDQpJIHdpbGwgdXNlIHRoZSB0aGUgcHJvcG9ydGlvbiBvZiBmZW1hbGUgd3JpdGVycyBhcyBhIG1haW4gcHJlZGljdG9yLCBidXQgYXMgY29udHJvbCB2YXJpYWJsZXMsIEkgd2lsbCBhbHNvIGFkZCB0aGUgZGVjYWRlIG9mIHJlbGVhc2UgYW5kIHRoZSBydW50aW1lIGluIG1pbnV0ZXMuIEEgYmlub21pYWwgbG9naXN0aWMgbW9kZWwgd2lsbCBiZSBmaXQgdG8gcHJlZGljdCB0aGUgcGFzc2luZyBvbiB0aGUgQmVjaGRlbCB0ZXN0LiANCg0KYGBge3J9DQoNCmJlY2hkZWxfbW9kZWwgPC0NCiAgICBnbG0oYmVjaGRlbF9wYXNzIH4gZmVtYWxlX3dyaXRlciArIGRlY2FkZSArIHJ1bnRpbWUsIA0KICAgICAgICBkYXRhID0gbW92aWVzLCANCiAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIikNCg0KIyBUaGlzIGlzIHRoZSBzdGFuZGFyZCByZWdyZXNzaW9uIG91dHB1dA0Kc3VtbWFyeShiZWNoZGVsX21vZGVsKQ0KDQojIFRoaXMgaXMgdGhlIHRpZHkgYXBwcm9hY2gsIHRoYXQgY29udmVydHMgbG9nIGxpa2VsaWhvb2RzIHRvIG9kZHMgcmF0aW9zLCBhbmQgYWRkcyBjb25maWRlbmNlIGludGVydmFscy4NCnRpZHkoYmVjaGRlbF9tb2RlbCwgY29uZi5pbnQgPSBUUlVFLCBleHBvbmVudGlhdGUgPSBUUlVFKQ0KDQojIFRoaXMgaXMgZm9yIGNvbnZlbmllbnRseSBtYWtpbmcgYW4gQVBBIGNvbXBhdGlibGUgc3VtbWFyeSB0YWJsZS4gV2UgYWxzbyB1c2Ugcm9idXN0IHN0YXRuZGFyZCBlcnJvcnMgdG8gYWNjb3VudCBmb3IgdGhlIGhldGVyb3NjZWRhc3RpY2l0eS4NCg0KdGFiX21vZGVsKGJlY2hkZWxfbW9kZWwsIA0KICAgICAgICAgIHNob3cuYWljID0gVFJVRSwNCiAgICAgICAgICBzaG93LmxvZ2xpayA9IFRSVUUsIA0KICAgICAgICAgIHN0cmluZy5jaSA9ICI5NSUgQ0kiLCANCiAgICAgICAgICBzaG93LnN0YXQgPSBUUlVFLA0KICAgICAgICAgIHJvYnVzdCA9IFRSVUUsDQogICAgICAgICAgZHYubGFiZWxzID0gIlBhc3NpbmcgdGhlIEJlY2hkZWwgdGVzdCIsDQogICAgICAgICAgcHJlZC5sYWJlbHMgPSBjKCIoSW50ZXJjZXB0KSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICIlIGZlbWFsZSBpbiB3cml0ZXIgdGVhbSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAiRGVjYWRlIG9mIHJlbGVhc2UiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIlJ1bnRpbWUgKGgpIikpDQoNCmBgYA0KDQpUaGUgcmVzdWx0cyByZXZlYWwgdGhhdCBhbGwgcHJlZGljdG9ycyBhcmUgc2lnbmlmaWNhbnQuIFRoaXMgbWVhbnMgdGhhdCB0aGUgY2hhbmNlIG9mIHBhc3NpbmcgdGhlIEJlY2hkZWwgdGVzdCBpcyA4LjIyLWZvbGQgaWYgdGhlIGVudGlyZSB3cml0ZXIgdGVhbSBpcyBmZW1hbGUsIGNvbXBhcmVkIHRvIGFuIGFsbCBtYWxlIHdyaXRlciB0ZWFtLiBUaGlzIGhvbGRzIGV2ZW4gaWYgd2UgYWNjb3VudCBmb3IgdGhlIGVmZmVjdHMgb2YgdGhlIHJlbGVhc2UgZGVjYWRlIChuZXdlciBtb3ZpZXMgcGFzcyB0aGUgdGVzdCBtb3JlIG9mdGVuKSwgYW5kIHJ1bnRpbWUgKGxvbmdlciBtb3ZpZXMgYXJlIG1vcmUgbGlrZWx5IHRvIGZhaWwgdGhlIHRlc3QsIHd0Zj8pLg0KDQpBc3N1bXB0aW9uIGNoZWNrcyBhcmUgYXZhaWxhYmxlIGZyb20gdGhlIGB7cGVyZm9ybWFuY2V9YCBwYWNrYWdlLCB0aGF0IGlzIHBhcnQgb2YgdGhlIGB7ZWFzeXN0YXRzfWAgZWNvc3lzdGVtLiBJIHdvbid0IHJlYWxseSBkbyBtdWNoIGFib3V0IHRoZSBhc3N1bXB0aW9uIGNoZWNrcyBub3csIG90aGVyIHRoYW4gc2hvd2luZyB0aGUgb3V0cHV0Lg0KDQpgYGB7cn0NCmNoZWNrX21vZGVsKGJlY2hkZWxfbW9kZWwpDQpgYGANCg0KIyMgRXZhbHVhdGUgbW9kZWwgcGVyZm9ybWFuY2UNCg0KSW4gZ2VuZXJhbCwgYHIgcGVyY2VudChtZWFuKG1vdmllcyRiZWNoZGVsX3Bhc3MpKWAgb2YgdGhlIGludmVzdGlnYXRlZCBtb3ZpZXMgcGFzc2VkIHRoZSBCZWNoZGVsIHRlc3QuIEZ1bGwgbWFsZSB0ZWFtcyBhcmUgdHdpY2UgYXMgbGlrZWx5IHRvIGZhaWwgdGhlIHRlc3QsIHdoaWxlIGZ1bGwgZmVtYWxlIHdyaXRlciB0ZWFtcyBhcmUgYXBwcm94aW1hdGVseSA0IHRpbWVzIG1vcmUgbGlrZWx5IHRvIHBhc3MgdGhlIHRlc3QuIFRodXMgdGhlcmUgaXMgYW4gOC1mb2xkIGRpZmZlcmVuY2UgYmV0d2VlbiBhbGwgbWFsZSBhbmQgYWxsIGZlbWFsZSB3cml0ZXIgdGVhbXMuDQoNCmBgYHtyfQ0KIyBDcmVhdGUgYSBwcmVkaWN0aW9uIGRhdGFzZXQgd2l0aCBhbGwgcG9zc2libGUgcHJlZGljdG9yIHZhbHVlcw0KbmV3ZGF0YSA8LQ0KICAgIGNyb3NzaW5nKGZlbWFsZV93cml0ZXIgPSBzZXEoMCwxLC4wMjUpLA0KICAgICAgICAgICAgIGRlY2FkZSA9IHNlcSgxOTcwLCAyMDEwLCAxMCksDQogICAgICAgICAgICAgcnVudGltZSA9IHVuaXF1ZShtb3ZpZXMkcnVudGltZSkpDQoNCiMgSW50ZXJwb2xhdGUgdGhlIHByZWRpY3RlZCB2YWx1ZXMsIGFuZCBjcmVhdGUgYQ0KYXVnbWVudChiZWNoZGVsX21vZGVsLCBuZXdkYXRhID0gbmV3ZGF0YSwgdHlwZS5wcmVkaWN0ID0gImxpbmsiKSAlPiUNCiAgICBncm91cF9ieShmZW1hbGVfd3JpdGVyKSAlPiUgDQogICAgbXV0YXRlKCAuZml0dGVkID0gZXhwKC5maXR0ZWQpLA0KICAgICAgICAgICAgYXZnX2ZpdHRlZCA9IG1lYW4oLmZpdHRlZCwgbmEucm0gPSBUUlVFKSkgJT4lIA0KICAgIHVuZ3JvdXAoKSAlPiUgDQogICAgZ2dwbG90KCkgKw0KICAgIGFlcyh4ID0gZmVtYWxlX3dyaXRlciwgeSA9IC5maXR0ZWQpICsNCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBjb2xvciA9ICJyZWQiLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgIGdlb21fcG9pbnQoYWxwaGEgPSAuMDEpICsNCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBhdmdfZml0dGVkKSwgY29sb3IgPSAiYmx1ZSIsIHNpemUgPSAyLCBhbHBoYSA9IC41KSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKw0KICAgIGxhYnMoeCA9ICIlIG9mIGZlbWFsZSB3cml0ZXJzIGZvciB0aGUgbW92aWUiLA0KICAgICAgICAgeSA9ICJPZGRzIHJhdGlvIG9mIHBhc3NpbmcgdGhlIEJlY2hkZWwgdGVzdCIsDQogICAgICAgICB0aXRsZSA9ICJQcm9wb3J0aW9uIG9mIGZlbWFsZSB3cml0ZXJzIHZzLiBwYXNzaW5nIHRoZSBCZWNoZGVsIHRlc3QgaW4gbW92aWVzIiwNCiAgICAgICAgIHN1YnRpdGxlID0gIldoZW4gYWxsIHdyaXRlcnMgYXJlIGZlbWFsZSwgcGFzc2luZyB0aGUgQmVjaGRlbCB0ZXN0IGlzIDggdGltZXMgbW9yZSBsaWtlbHkgY29tcGFyZWQgdG8gYW4gYWxsIG1hbGUgd3JpdGVyIHRlYW0uIikNCmBgYA0KDQojIyMgUk9DIGN1cnZlDQoNCkZpbmFsbHksIHdlIHdpbGwgY3JlYXRlIGEgUk9DIGN1cnZlIGFuZCBjYWxjdWxhdGUgaXRzIEFVQyB0byBzZWUgaG93IGVmZmljaWVudGx5IG91ciBtb2RlbCBjYW4gcHJlZGljdCB0aGUgb3V0Y29tZSwgYmFzZWQgb24gdGhlIHByZWRpY3RvcnMuDQoNCmBgYHtyfQ0KbW9kX3ByZWQgPC0NCiAgICBhdWdtZW50KGJlY2hkZWxfbW9kZWwsIHR5cGUucHJlZGljdCA9ICJyZXNwb25zZSIpICU+JSANCiAgICB0cmFuc211dGUodHJ1dGggPSBhcy5mYWN0b3IoYmVjaGRlbF9wYXNzKSwgDQogICAgICAgICAgICAgIGVzdGltYXRlID0gLmZpdHRlZCkNCmBgYA0KDQoNClRoZSBBVUMgZm9yIHRoZSBtb2RlbCBpcyBvaywgYnV0IG5vdCBncmVhdDogYHIgcm9jX2F1YyhkYXRhID0gbW9kX3ByZWQsIHRydXRoLCBlc3RpbWF0ZSwgZXZlbnRfbGV2ZWwgPSAic2Vjb25kIikkLmVzdGltYXRlICU+JSBwZXJjZW50KClgLg0KDQpgYGB7cn0NCnJvY19iZWNoZGVsIDwtIHJvY19jdXJ2ZShtb2RfcHJlZCwgdHJ1dGgsIGVzdGltYXRlLCBldmVudF9sZXZlbCA9ICJzZWNvbmQiKQ0KICAgIA0KYXV0b3Bsb3Qocm9jX2JlY2hkZWwpICsNCiAgICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gcGVyY2VudF9mb3JtYXQoKSkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBwZXJjZW50X2Zvcm1hdCgpKSArDQogICAgbGFicyh0aXRsZSA9ICJST0MgY3VydmUgZm9yIHRoZSBtb2RlbCIpDQoNCg0KYGBgDQoNCiMgSW1wcm92ZW1lbnQgaWRlYXMNCg0KLSBJdCB3b3VsZCBiZSBwb3NzaWJsZSB0byBjcmVhdGUgYSBtb3JlIGNvbXByZWhlbnNpdmUgcHJvcG9ydGlvbiBvZiBmZW1hbGVzIHZhcmlhYmxlLCB0YWtpbmcgaW50byBhY2NvdW50IHRoZSBkaXJlY3RvcihzKSBhbG9uZyB3aXRoIHRoZSB3cml0ZXJzLiANCi0gR2VuZGVyIGNsYXNzaWZpY2F0aW9uIGNvdWxkIGJlIGltcHJvdmVkIGJ5IHRpbmtlcmluZyB3aXRoIHRoZSBwYXJhbWV0ZXJzIG9mIHRoZSBnZW5kZXIgcGFja2FnZS4NCi0gT3RoZXIgdHlwZXMgb2YgbW9kZWxzIGNvdWxkIGJlIG1vcmUgdXNlZnVsLCBlLmcuIHJhbmRvbSBmb3Jlc3QuDQotIFRoZSB3b3JkcyBmcm9tIHRoZSBkZXNjcmlwdGlvbiBvZiB0aGUgbW92aWUgY291bGQgYmUgYWRkZWQgYXMgcHJlZGljdG9ycy4=