
Introduction
Whether this is the tour the force, a great number or peculiar
gadgets or glasses of cold vodka Martni, there are definitely many
aspects that constitute a great James Bond movie. Having watched A
spy who loved me (1977) recently, I’ve begun to wonder if that
could be somehow figured out with the help of machine learning. This is
my small summer data science project that aims to predict the average
IMDB score for the newest 007 movie No time to die and see what
contributes to the score.
More formally, we will be looking at a regression problem for a small
dataset (24 items) and training a random forest with cross validation.
Then we will examine the break down plot for a previously unseen example
- the new movie. I will be using caret package for the model
and DALEX for explanation.
1. The data: For your eyes only
data(JamesBond)
df <- JamesBond %>% select(-US_Gross, -World_Gross, -US_Adj, -Budget, -Avg_User_Rtn_Tom) %>%
relocate(Avg_User_IMDB, .after = last_col())
I will be using the James Bond dataset from the HoRM package. See resources
Here we have the data of the previous 24 movies. I’m going to use 12 ouf
of the 18 available features, namely: Year, Movie, Bond, World_Adj,
Budget_Adj, Film_Length, Conquests, Martinis, BJB, Kills_Bond,
Kills_Others, Top_100, Avg_User_IMDB.
To clear out the feature names:
- World_Adj The film’s 2013-adjusted worldwide gross
(in 1000’s of U.S. dollars).
- Budget_Adj The film’s 2013-adjusted budget (in 1000’s
of U.S. dollars).
- Avg_User_IMDB The average user rating on IMDB
(www.imdb.com).
- Conquests The number of “conquests” by Bond in the
film.
- Martinis The number of martinis Bond drank in the
film.
- BJB The number of times Bond stated “Bond. James
Bond.” in the movie.
- Kills_Bond The number of people killed by Bond.
- Kills_Others The number of people killed in the film
by people other than Bond.
- Top_100 An indicator where a value of 1 means the
title song within the top 100 on the UK Singles Chart and the U.S.
Billboard Hot 100 and a value of 0 means it did not.
kable(df)
| 1962 |
Dr. No |
Sean Connery |
457928 |
7688 |
110 |
3 |
2 |
1 |
4 |
8 |
0 |
7.3 |
| 1963 |
From Russia with Love |
Sean Connery |
598624 |
15174 |
115 |
4 |
0 |
0 |
11 |
16 |
0 |
7.5 |
| 1964 |
Goldfinger |
Sean Connery |
935404 |
22468 |
110 |
2 |
1 |
2 |
9 |
68 |
1 |
7.8 |
| 1965 |
Thunderball |
Sean Connery |
1040693 |
66333 |
130 |
3 |
0 |
0 |
20 |
90 |
1 |
7.0 |
| 1967 |
You Only Live Twice |
Sean Connery |
775740 |
66035 |
117 |
3 |
1 |
0 |
21 |
175 |
1 |
6.9 |
| 1969 |
On Her Majesty’s Secret Service |
George Lazenby |
518736 |
50608 |
142 |
3 |
1 |
2 |
5 |
37 |
0 |
6.8 |
| 1971 |
Diamonds Are Forever |
Sean Connery |
664969 |
41274 |
120 |
1 |
0 |
1 |
7 |
42 |
1 |
6.7 |
| 1973 |
Live and Let Die |
Roger Moore |
846046 |
36603 |
121 |
3 |
0 |
1 |
8 |
5 |
1 |
6.8 |
| 1974 |
The Man with the Golden Gun |
Roger Moore |
459623 |
32965 |
125 |
2 |
0 |
2 |
1 |
5 |
0 |
6.7 |
| 1977 |
The Spy Who Loved Me |
Roger Moore |
710290 |
53636 |
125 |
3 |
1 |
1 |
31 |
116 |
1 |
7.1 |
| 1979 |
Moonraker |
Roger Moore |
672514 |
99134 |
126 |
3 |
1 |
1 |
12 |
69 |
0 |
6.2 |
| 1981 |
For Your Eyes Only |
Roger Moore |
498812 |
71514 |
127 |
2 |
0 |
2 |
18 |
36 |
1 |
6.8 |
| 1983 |
Octopussy |
Roger Moore |
437059 |
64102 |
131 |
2 |
0 |
1 |
15 |
43 |
1 |
6.5 |
| 1985 |
A View to a Kill |
Roger Moore |
329322 |
64730 |
131 |
4 |
0 |
2 |
5 |
57 |
1 |
6.2 |
| 1987 |
The Living Daylights |
Timothy Dalton |
390758 |
81749 |
130 |
2 |
2 |
1 |
13 |
29 |
0 |
6.7 |
| 1989 |
License to Kill |
Timothy Dalton |
292392 |
78637 |
133 |
2 |
1 |
1 |
10 |
13 |
0 |
6.5 |
| 1995 |
Goldeneye |
Pierce Brosnan |
542985 |
91404 |
130 |
2 |
1 |
1 |
47 |
25 |
0 |
7.2 |
| 1997 |
Tomorrow Never Dies |
Pierce Brosnan |
491098 |
159117 |
119 |
3 |
1 |
1 |
30 |
24 |
0 |
6.4 |
| 1999 |
The World Is Not Enough |
Pierce Brosnan |
504091 |
188130 |
128 |
3 |
1 |
2 |
27 |
43 |
0 |
6.3 |
| 2002 |
Die Another Day |
Pierce Brosnan |
557433 |
183255 |
133 |
2 |
2 |
1 |
31 |
20 |
1 |
6.0 |
| 2006 |
Casino Royale |
Daniel Craig |
686784 |
117465 |
144 |
2 |
3 |
1 |
11 |
11 |
1 |
7.9 |
| 2008 |
Quantum of Solace |
Daniel Craig |
638035 |
248014 |
106 |
1 |
6 |
0 |
16 |
15 |
1 |
6.7 |
| 2012 |
Skyfall |
Daniel Craig |
1120980 |
202240 |
143 |
3 |
1 |
1 |
26 |
26 |
1 |
7.8 |
| 2015 |
Spectre |
Daniel Craig |
864553 |
240803 |
148 |
3 |
1 |
1 |
30 |
205 |
1 |
6.8 |
2. Charts: Live and let die
In this section we will look at some interesting features. Hover over
the bullet marker to get extra information about the movie.
plot_df <- JamesBond
plot_df$Movie <- factor (plot_df$Movie , levels = plot_df$Movie) # we want to preserve the dataframe's chronological movie ordering
m <- list(
l = 100,
r = 50,
b = 80,
t = 150,
pad = 2
)
fig <- plot_ly(plot_df, x = ~Year, y = ~Martinis, type= 'scatter', mode= 'lines+markers',
line = list(color = 'rgba(49,130,189, 1)', width = 4),
marker = list(color = 'rgba(49,130,189, 1)', size = 12),
hoverinfo = 'text',
text = ~paste(Movie, Year)) %>%
layout(title = "Martini drinks Bond consumes in the movie",
font=list(size = 20),
margin = m,
xaxis = list(
showgrid = FALSE,
zeroline = FALSE,
tickfont = list(size = 16)
),
yaxis = list(
showgrid = FALSE,
zeroline = TRUE
))
fig
plot_df <- JamesBond
plot_df$Movie <- factor (plot_df$Movie , levels = plot_df$Movie)
fig2 <- plot_ly(plot_df, x = ~Year, y = ~Kills_Bond, name = 'By Bond', type= 'scatter', mode= 'lines+markers',
line = list(color = 'rgba(166,4,4,1)', width = 4),
marker = list(color = 'rgba(166,4,4,1)', size = 12),
hoverinfo = 'text',
text = ~paste(Movie, Year, "<br>Kills:", Kills_Bond)) %>%
add_trace(y = ~Kills_Others, type = 'scatter', mode= 'lines+markers',
name = "By Others",
line = list(color = 'rgba(192,172,182,1)', width = 4),
marker = list(color = 'rgba(192,172,182,1)', size = 12),
hoverinfo = 'text',
text = ~paste(Movie, Year, "<br>Kills:", Kills_Others)) %>%
layout(title = "Kills in 007 movies",
font=list(size = 20),
margin = m,
xaxis = list(
showgrid = FALSE,
zeroline = FALSE,
tickfont = list(size = 16)
),
yaxis = list(
title.text = '',
showgrid = FALSE,
zeroline = TRUE
))
fig2
The most correlated feature with the average IMDB score is the World
gross, which should be expected. It is interesting to note that Kills,
Film Length and Budget are growing in the newer movies.
fig_corr <- JamesBond %>% select(-US_Gross, -World_Gross, -US_Adj, -Budget, -Avg_User_Rtn_Tom, -Movie, -Bond) %>%
relocate(Avg_User_IMDB, .after = last_col()) %>%
cor() %>%
ggcorrplot(method = 'square', type = 'upper', outline.color = 'white', lab_size = 8,
title= "Feature Correlations",
legend.title = "Corelation",
tl.cex = 18,
lab=TRUE) +
theme( text = element_text(size = 22))
fig_corr

3. Regression: The world is not enough
It is time to train our regressor. We will use random forest with
cross-validation. Next, we will create a DALEX explainer to examine the
Feature Importance.
data(JamesBond)
df <- JamesBond %>% select(-Movie, -US_Gross, -World_Gross, -US_Adj, -Budget, -Avg_User_Rtn_Tom) %>%
relocate(Avg_User_IMDB, .after = last_col())
train_control <- trainControl(method = "cv", number = 8, savePredictions = "all")
model_rf <- train(Avg_User_IMDB~. , data = df, method = "rf", ntree = 100, trControl = train_control)
explainer_rf <- DALEX::explain(model = model_rf,
label = "rf",
verbose = FALSE,
y = df$Avg_User_IMDB
)
print(model_performance(explainer_rf))
## Measures for: regression
## mse : 0.05157403
## rmse : 0.2270992
## r2 : 0.7990078
## mad : 0.1360167
##
## Residuals:
## 0% 10% 20% 30% 40% 50%
## -0.38593333 -0.30377500 -0.19552000 -0.12311000 -0.07096667 -0.03891667
## 60% 70% 80% 90% 100%
## -0.01014000 0.07145333 0.13466333 0.31706333 0.49035000
plot(model_parts(explainer_rf, loss_function = loss_root_mean_square)) + theme(text = element_text(size = 20))

4. Break down: No time to die
I found the necessary statistics from No time to die (2021).
As some were hard to find, I assumed the values of Martinis and BJBs.
The gross and budget were adjusted to 2013 for consistency:
no_time_to_die <- data.frame(2021, "Daniel Craig", 608510000, 214930000, 163, 0, 1, 1, 66, 109, 1, 0)
colnames(no_time_to_die) <- colnames(df)
df <- rbind(df, no_time_to_die)
kable(no_time_to_die)
| 2021 |
Daniel Craig |
608510000 |
214930000 |
163 |
0 |
1 |
1 |
66 |
109 |
1 |
0 |
predict(explainer_rf, tail(df, n=1))
## 25
## 7.347983
pb = predict_parts(explainer = explainer_rf, tail(df, n=1), type= "break_down")
plot(pb)

Let’s examine the contribution of each feature of our example, the
25th 007 movie. What influances the final score the most is the world
gross (+), year (+), length (+) and budget (-). My favorite features,
introductions and Martinis, are unfortunately not as significant, but
they are an advantage (who would have thought!).
We are very close to the true average IMDB rating! (https://www.imdb.com/title/tt2382320/, August 2022).
Remark: This is just a toy example with a small
dataset and it should be further tested on more examples to asses the
model’s quality. Though it is definitely not enough to consider the
rating of a movie based on the number of drinks consumed or main
character’s introductions (even if this is Bond, James Bond), I hope
this project offers quality entertainment and pleasing
visualizations.
From Poland with love,
Kacper Dobek
https://github.com/kapiblue
LS0tCnRpdGxlOiAiV0hBVCBNQUtFUyBBIEdSRUFUIEpBTUVTIEJPTkQgTU9WSUU/IgphdXRob3I6ICJLYWNwZXIgRG9iZWsiCmRhdGU6ICIyMDIyLTA4LTIzIgpvdXRwdXQ6IAogICAgaHRtbF9kb2N1bWVudDoKICAgICAgICBjc3M6ICJyZXNvdXJjZXMvc3R5bGUuY3NzIgogICAgICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgICAgICB0b2M6IHRydWUKICAgICAgICBvdXRfd2lkdGg6IDgwJQogICAgICAgIAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGluY2x1ZGUgPSBUUlVFLCBjYWNoZSA9IFRSVUUpCmxpYnJhcnkoSG9STSkKbGlicmFyeShkcGx5cikKbGlicmFyeShrbml0cikKbGlicmFyeShEQUxFWCkKbGlicmFyeShjYXJldCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdnY29ycnBsb3QpCmxpYnJhcnkocGxvdGx5KQpzZXQuc2VlZCgwMDcpCmBgYAoKYGBge3IgaW1nLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygncmVzb3VyY2VzLzAwNy5wbmcnKQpgYGAKCiMjIEludHJvZHVjdGlvbgoKPiBXaGV0aGVyIHRoaXMgaXMgdGhlIHRvdXIgdGhlIGZvcmNlLCBhIGdyZWF0IG51bWJlciBvciBwZWN1bGlhciBnYWRnZXRzIG9yIGdsYXNzZXMgb2YgY29sZCB2b2RrYSBNYXJ0bmksIHRoZXJlIGFyZSBkZWZpbml0ZWx5IG1hbnkgYXNwZWN0cyB0aGF0IGNvbnN0aXR1dGUgYSBncmVhdCBKYW1lcyBCb25kIG1vdmllLiBIYXZpbmcgd2F0Y2hlZCAqQSBzcHkgd2hvIGxvdmVkIG1lICgxOTc3KSogcmVjZW50bHksIEkndmUgYmVndW4gdG8gd29uZGVyIGlmIHRoYXQgY291bGQgYmUgc29tZWhvdyBmaWd1cmVkIG91dCB3aXRoIHRoZSBoZWxwIG9mIG1hY2hpbmUgbGVhcm5pbmcuIFRoaXMgaXMgbXkgc21hbGwgc3VtbWVyIGRhdGEgc2NpZW5jZSBwcm9qZWN0IHRoYXQgYWltcyB0byBwcmVkaWN0IHRoZSBhdmVyYWdlIElNREIgc2NvcmUgZm9yIHRoZSBuZXdlc3QgMDA3IG1vdmllICpObyB0aW1lIHRvIGRpZSogYW5kIHNlZSB3aGF0IGNvbnRyaWJ1dGVzIHRvIHRoZSBzY29yZS4KCj4gTW9yZSBmb3JtYWxseSwgd2Ugd2lsbCBiZSBsb29raW5nIGF0IGEgcmVncmVzc2lvbiBwcm9ibGVtIGZvciBhIHNtYWxsIGRhdGFzZXQgKDI0IGl0ZW1zKSBhbmQgdHJhaW5pbmcgYSByYW5kb20gZm9yZXN0IHdpdGggY3Jvc3MgdmFsaWRhdGlvbi4gVGhlbiB3ZSB3aWxsIGV4YW1pbmUgdGhlIGJyZWFrIGRvd24gcGxvdCBmb3IgYSBwcmV2aW91c2x5IHVuc2VlbiBleGFtcGxlIC0gdGhlIG5ldyBtb3ZpZS4gSSB3aWxsIGJlIHVzaW5nICpjYXJldCogcGFja2FnZSBmb3IgdGhlIG1vZGVsIGFuZCAqREFMRVgqIGZvciBleHBsYW5hdGlvbi4KCiMjIDEuIFRoZSBkYXRhOiBGb3IgeW91ciBleWVzIG9ubHkKCmBgYHtyIGRhdGFwcmVwfQpkYXRhKEphbWVzQm9uZCkKZGYgPC0gSmFtZXNCb25kICU+JSBzZWxlY3QoLVVTX0dyb3NzLCAtV29ybGRfR3Jvc3MsIC1VU19BZGosIC1CdWRnZXQsIC1BdmdfVXNlcl9SdG5fVG9tKSAlPiUKICByZWxvY2F0ZShBdmdfVXNlcl9JTURCLCAuYWZ0ZXIgPSBsYXN0X2NvbCgpKQpgYGAKCkkgd2lsbCBiZSB1c2luZyB0aGUgSmFtZXMgQm9uZCBkYXRhc2V0IGZyb20gdGhlIEhvUk0gcGFja2FnZS4gW1NlZSByZXNvdXJjZXNdKCNyZXNvdXJjZXMpXApIZXJlIHdlIGhhdmUgdGhlIGRhdGEgb2YgdGhlIHByZXZpb3VzIDI0IG1vdmllcy4gSSdtIGdvaW5nIHRvIHVzZSAxMiBvdWYgb2YgdGhlIDE4IGF2YWlsYWJsZSBmZWF0dXJlcywgbmFtZWx5OiBgciBjb2xuYW1lcyhkZilgLlwKVG8gY2xlYXIgb3V0IHRoZSBmZWF0dXJlIG5hbWVzOlwKLSAqKldvcmxkX0FkaioqIFRoZSBmaWxtJ3MgMjAxMy1hZGp1c3RlZCB3b3JsZHdpZGUgZ3Jvc3MgKGluIDEwMDAncyBvZiBVLlMuIGRvbGxhcnMpLlwKLSAqKkJ1ZGdldF9BZGoqKiBUaGUgZmlsbSdzIDIwMTMtYWRqdXN0ZWQgYnVkZ2V0IChpbiAxMDAwJ3Mgb2YgVS5TLiBkb2xsYXJzKS5cCi0gKipBdmdfVXNlcl9JTURCKiogVGhlIGF2ZXJhZ2UgdXNlciByYXRpbmcgb24gSU1EQiAod3d3LmltZGIuY29tKS5cCi0gKipDb25xdWVzdHMqKiBUaGUgbnVtYmVyIG9mICJjb25xdWVzdHMiIGJ5IEJvbmQgaW4gdGhlIGZpbG0uXAotICoqTWFydGluaXMqKiBUaGUgbnVtYmVyIG9mIG1hcnRpbmlzIEJvbmQgZHJhbmsgaW4gdGhlIGZpbG0uXAotICoqQkpCKiogVGhlIG51bWJlciBvZiB0aW1lcyBCb25kIHN0YXRlZCAiQm9uZC4gSmFtZXMgQm9uZC4iIGluIHRoZSBtb3ZpZS5cCi0gKipLaWxsc19Cb25kKiogVGhlIG51bWJlciBvZiBwZW9wbGUga2lsbGVkIGJ5IEJvbmQuXAotICoqS2lsbHNfT3RoZXJzKiogVGhlIG51bWJlciBvZiBwZW9wbGUga2lsbGVkIGluIHRoZSBmaWxtIGJ5IHBlb3BsZSBvdGhlciB0aGFuIEJvbmQuXAotICoqVG9wXzEwMCoqIEFuIGluZGljYXRvciB3aGVyZSBhIHZhbHVlIG9mIDEgbWVhbnMgdGhlIHRpdGxlIHNvbmcgd2l0aGluIHRoZSB0b3AgMTAwIG9uIHRoZSBVSyBTaW5nbGVzIENoYXJ0IGFuZCB0aGUgVS5TLiBCaWxsYm9hcmQgSG90IDEwMCBhbmQgYSB2YWx1ZSBvZiAwIG1lYW5zIGl0IGRpZCBub3QuXAoKCgpgYGB7ciB0YWJsZSwgZmlnLndpZHRoID0gMTJ9CmthYmxlKGRmKQpgYGAKCiMjIDIuIENoYXJ0czogTGl2ZSBhbmQgbGV0IGRpZQoKSW4gdGhpcyBzZWN0aW9uIHdlIHdpbGwgbG9vayBhdCBzb21lIGludGVyZXN0aW5nIGZlYXR1cmVzLiBIb3ZlciBvdmVyIHRoZSBidWxsZXQgbWFya2VyIHRvIGdldCBleHRyYSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgbW92aWUuCgpgYGB7ciBtYXJ0aW5pcywgZmlnLndpZHRoPSAxMiwgZmlnLmhlaWdodD0gN30KcGxvdF9kZiA8LSBKYW1lc0JvbmQKcGxvdF9kZiRNb3ZpZSA8LSBmYWN0b3IgKHBsb3RfZGYkTW92aWUgLCBsZXZlbHMgPSBwbG90X2RmJE1vdmllKSAjIHdlIHdhbnQgdG8gcHJlc2VydmUgdGhlIGRhdGFmcmFtZSdzIGNocm9ub2xvZ2ljYWwgbW92aWUgb3JkZXJpbmcKCm0gPC0gbGlzdCgKICBsID0gMTAwLAogIHIgPSA1MCwKICBiID0gODAsCiAgdCA9IDE1MCwKICBwYWQgPSAyCikKCmZpZyA8LSBwbG90X2x5KHBsb3RfZGYsIHggPSB+WWVhciwgeSA9IH5NYXJ0aW5pcywgdHlwZT0gJ3NjYXR0ZXInLCBtb2RlPSAnbGluZXMrbWFya2VycycsCiAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gJ3JnYmEoNDksMTMwLDE4OSwgMSknLCB3aWR0aCA9IDQpLAogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gJ3JnYmEoNDksMTMwLDE4OSwgMSknLCBzaXplID0gMTIpLAogICAgICAgICAgICAgICBob3ZlcmluZm8gPSAndGV4dCcsCiAgICAgICAgICAgICAgIHRleHQgPSB+cGFzdGUoTW92aWUsIFllYXIpKSAlPiUKICBsYXlvdXQodGl0bGUgPSAiTWFydGluaSBkcmlua3MgQm9uZCBjb25zdW1lcyBpbiB0aGUgbW92aWUiLAogICAgICAgICBmb250PWxpc3Qoc2l6ZSA9IDIwKSwKICAgICAgICAgbWFyZ2luID0gbSwKICAgICAgICAgeGF4aXMgPSBsaXN0KAogICAgICAgICAgIHNob3dncmlkID0gRkFMU0UsCiAgICAgICAgICAgemVyb2xpbmUgPSBGQUxTRSwKICAgICAgICAgICB0aWNrZm9udCA9IGxpc3Qoc2l6ZSA9IDE2KQogICAgICAgICApLAogICAgICAgICB5YXhpcyA9IGxpc3QoCiAgICAgICAgICAgc2hvd2dyaWQgPSBGQUxTRSwKICAgICAgICAgICB6ZXJvbGluZSA9IFRSVUUgCiAgICAgICAgICkpCmZpZwpgYGAKCmBgYHtyIGtpbGxzLCBmaWcud2lkdGg9IDEyLCBmaWcuaGVpZ2h0PSA4fQoKcGxvdF9kZiA8LSBKYW1lc0JvbmQKcGxvdF9kZiRNb3ZpZSA8LSBmYWN0b3IgKHBsb3RfZGYkTW92aWUgLCBsZXZlbHMgPSBwbG90X2RmJE1vdmllKQoKZmlnMiA8LSBwbG90X2x5KHBsb3RfZGYsIHggPSB+WWVhciwgeSA9IH5LaWxsc19Cb25kLCBuYW1lID0gJ0J5IEJvbmQnLCB0eXBlPSAnc2NhdHRlcicsIG1vZGU9ICdsaW5lcyttYXJrZXJzJywKICAgICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAncmdiYSgxNjYsNCw0LDEpJywgd2lkdGggPSA0KSwKICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICdyZ2JhKDE2Niw0LDQsMSknLCBzaXplID0gMTIpLAogICAgICAgICAgICAgICBob3ZlcmluZm8gPSAndGV4dCcsCiAgICAgICAgICAgICAgIHRleHQgPSB+cGFzdGUoTW92aWUsIFllYXIsICI8YnI+S2lsbHM6IiwgS2lsbHNfQm9uZCkpICU+JQogIGFkZF90cmFjZSh5ID0gfktpbGxzX090aGVycywgIHR5cGUgPSAnc2NhdHRlcicsIG1vZGU9ICdsaW5lcyttYXJrZXJzJywKICAgICAgICAgICAgICAgbmFtZSA9ICJCeSBPdGhlcnMiLAogICAgICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICdyZ2JhKDE5MiwxNzIsMTgyLDEpJywgd2lkdGggPSA0KSwKICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICdyZ2JhKDE5MiwxNzIsMTgyLDEpJywgc2l6ZSA9IDEyKSwKICAgICAgICAgICAgICAgaG92ZXJpbmZvID0gJ3RleHQnLAogICAgICAgICAgICAgICB0ZXh0ID0gfnBhc3RlKE1vdmllLCBZZWFyLCAiPGJyPktpbGxzOiIsIEtpbGxzX090aGVycykpICU+JQogIGxheW91dCh0aXRsZSA9ICJLaWxscyBpbiAwMDcgbW92aWVzIiwKICAgICAgICAgZm9udD1saXN0KHNpemUgPSAyMCksCiAgICAgICAgIG1hcmdpbiA9IG0sCiAgICAgICAgIHhheGlzID0gbGlzdCgKICAgICAgICAgICBzaG93Z3JpZCA9IEZBTFNFLAogICAgICAgICAgIHplcm9saW5lID0gRkFMU0UsCiAgICAgICAgICAgdGlja2ZvbnQgPSBsaXN0KHNpemUgPSAxNikKICAgICAgICAgKSwKICAgICAgICAgeWF4aXMgPSBsaXN0KAogICAgICAgICAgIHRpdGxlLnRleHQgPSAnJywKICAgICAgICAgICBzaG93Z3JpZCA9IEZBTFNFLAogICAgICAgICAgIHplcm9saW5lID0gVFJVRQogICAgICAgICApKQpmaWcyCmBgYApUaGUgbW9zdCBjb3JyZWxhdGVkIGZlYXR1cmUgd2l0aCB0aGUgYXZlcmFnZSBJTURCIHNjb3JlIGlzIHRoZSBXb3JsZCBncm9zcywgd2hpY2ggc2hvdWxkIGJlIGV4cGVjdGVkLiBJdCBpcyBpbnRlcmVzdGluZyB0byBub3RlIHRoYXQgS2lsbHMsIEZpbG0gTGVuZ3RoIGFuZCBCdWRnZXQgYXJlIGdyb3dpbmcgaW4gdGhlIG5ld2VyIG1vdmllcy4gCgpgYGB7ciBjb3JyLCBmaWcud2lkdGggPSAxOCwgZmlnLmhlaWdodCA9IDEyLCB3YXJuaW5nPUZBTFNFfQoKZmlnX2NvcnIgPC0gSmFtZXNCb25kICU+JSBzZWxlY3QoLVVTX0dyb3NzLCAtV29ybGRfR3Jvc3MsIC1VU19BZGosIC1CdWRnZXQsIC1BdmdfVXNlcl9SdG5fVG9tLCAtTW92aWUsIC1Cb25kKSAlPiUKICByZWxvY2F0ZShBdmdfVXNlcl9JTURCLCAuYWZ0ZXIgPSBsYXN0X2NvbCgpKSAlPiUKICBjb3IoKSAlPiUgCiAgZ2djb3JycGxvdChtZXRob2QgPSAnc3F1YXJlJywgdHlwZSA9ICd1cHBlcicsIG91dGxpbmUuY29sb3IgPSAnd2hpdGUnLCBsYWJfc2l6ZSA9IDgsCiAgICAgICAgICAgICB0aXRsZT0gIkZlYXR1cmUgQ29ycmVsYXRpb25zIiwKICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9ICJDb3JlbGF0aW9uIiwKICAgICAgICAgICAgIHRsLmNleCA9IDE4LAogICAgICAgICAgICAgbGFiPVRSVUUpICsKICB0aGVtZSggdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjIpKQoKZmlnX2NvcnIKYGBgCgojIyAzLiBSZWdyZXNzaW9uOiBUaGUgd29ybGQgaXMgbm90IGVub3VnaAoKSXQgaXMgdGltZSB0byB0cmFpbiBvdXIgcmVncmVzc29yLiBXZSB3aWxsIHVzZSByYW5kb20gZm9yZXN0IHdpdGggY3Jvc3MtdmFsaWRhdGlvbi4gTmV4dCwgd2Ugd2lsbCBjcmVhdGUgYSBEQUxFWCBleHBsYWluZXIgdG8gZXhhbWluZSB0aGUgRmVhdHVyZSBJbXBvcnRhbmNlLgoKYGBge3IgY2FyZXQsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0PSA3LCBvdXQud2lkdGg9JzEwMCUnfQoKZGF0YShKYW1lc0JvbmQpCmRmIDwtIEphbWVzQm9uZCAlPiUgc2VsZWN0KC1Nb3ZpZSwgLVVTX0dyb3NzLCAtV29ybGRfR3Jvc3MsIC1VU19BZGosIC1CdWRnZXQsIC1BdmdfVXNlcl9SdG5fVG9tKSAlPiUKICByZWxvY2F0ZShBdmdfVXNlcl9JTURCLCAuYWZ0ZXIgPSBsYXN0X2NvbCgpKQoKdHJhaW5fY29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gOCwgc2F2ZVByZWRpY3Rpb25zID0gImFsbCIpCgptb2RlbF9yZiA8LSB0cmFpbihBdmdfVXNlcl9JTURCfi4gLCBkYXRhID0gZGYsIG1ldGhvZCA9ICJyZiIsIG50cmVlID0gMTAwLCB0ckNvbnRyb2wgPSB0cmFpbl9jb250cm9sKQoKZXhwbGFpbmVyX3JmIDwtIERBTEVYOjpleHBsYWluKG1vZGVsID0gbW9kZWxfcmYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gInJmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZGYkQXZnX1VzZXJfSU1EQgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpwcmludChtb2RlbF9wZXJmb3JtYW5jZShleHBsYWluZXJfcmYpKQoKCnBsb3QobW9kZWxfcGFydHMoZXhwbGFpbmVyX3JmLCBsb3NzX2Z1bmN0aW9uID0gbG9zc19yb290X21lYW5fc3F1YXJlKSkgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpCmBgYAoKIyMgNC4gQnJlYWsgZG93bjogTm8gdGltZSB0byBkaWUgCgpJIGZvdW5kIHRoZSBuZWNlc3Nhcnkgc3RhdGlzdGljcyBmcm9tICpObyB0aW1lIHRvIGRpZSAoMjAyMSkqLiBBcyBzb21lIHdlcmUgaGFyZCB0byBmaW5kLCBJIGFzc3VtZWQgdGhlIHZhbHVlcyBvZiBNYXJ0aW5pcyBhbmQgQkpCcy4gVGhlIGdyb3NzIGFuZCBidWRnZXQgd2VyZSBhZGp1c3RlZCB0byAyMDEzIGZvciBjb25zaXN0ZW5jeToKCmBgYHtyIGJyZWFrZG93biwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQ9IDcsIG91dC53aWR0aD0nMTAwJSd9Cm5vX3RpbWVfdG9fZGllIDwtIGRhdGEuZnJhbWUoMjAyMSwgIkRhbmllbCBDcmFpZyIsIDYwODUxMDAwMCwgMjE0OTMwMDAwLCAxNjMsIDAsIDEsIDEsIDY2LCAxMDksIDEsIDApCmNvbG5hbWVzKG5vX3RpbWVfdG9fZGllKSA8LSBjb2xuYW1lcyhkZikKZGYgPC0gcmJpbmQoZGYsIG5vX3RpbWVfdG9fZGllKQoKa2FibGUobm9fdGltZV90b19kaWUpCgpwcmVkaWN0KGV4cGxhaW5lcl9yZiwgdGFpbChkZiwgbj0xKSkKCnBiID0gcHJlZGljdF9wYXJ0cyhleHBsYWluZXIgPSBleHBsYWluZXJfcmYsIHRhaWwoZGYsIG49MSksIHR5cGU9ICJicmVha19kb3duIikKcGxvdChwYikKYGBgCgpMZXQncyBleGFtaW5lIHRoZSBjb250cmlidXRpb24gb2YgZWFjaCBmZWF0dXJlIG9mIG91ciBleGFtcGxlLCB0aGUgMjV0aCAwMDcgbW92aWUuIFdoYXQgaW5mbHVhbmNlcyB0aGUgZmluYWwgc2NvcmUgdGhlIG1vc3QgaXMgdGhlIHdvcmxkIGdyb3NzICgrKSwgeWVhciAoKyksIGxlbmd0aCAoKykgYW5kIGJ1ZGdldCAoLSkuIE15IGZhdm9yaXRlIGZlYXR1cmVzLCBpbnRyb2R1Y3Rpb25zIGFuZCBNYXJ0aW5pcywgYXJlIHVuZm9ydHVuYXRlbHkgbm90IGFzIHNpZ25pZmljYW50LCBidXQgdGhleSBhcmUgYW4gYWR2YW50YWdlICh3aG8gd291bGQgaGF2ZSB0aG91Z2h0ISkuCgo+ICBXZSBhcmUgdmVyeSBjbG9zZSB0byB0aGUgdHJ1ZSBhdmVyYWdlIElNREIgcmF0aW5nISAoaHR0cHM6Ly93d3cuaW1kYi5jb20vdGl0bGUvdHQyMzgyMzIwLywgQXVndXN0IDIwMjIpLiAKCioqUmVtYXJrOioqIFRoaXMgaXMganVzdCBhIHRveSBleGFtcGxlIHdpdGggYSBzbWFsbCBkYXRhc2V0IGFuZCBpdCBzaG91bGQgYmUgZnVydGhlciB0ZXN0ZWQgb24gbW9yZSBleGFtcGxlcyB0byBhc3NlcyB0aGUgbW9kZWwncyBxdWFsaXR5LiBUaG91Z2ggaXQgaXMgZGVmaW5pdGVseSBub3QgZW5vdWdoIHRvIGNvbnNpZGVyIHRoZSByYXRpbmcgb2YgYSBtb3ZpZSBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIGRyaW5rcyBjb25zdW1lZCBvciBtYWluIGNoYXJhY3RlcidzIGludHJvZHVjdGlvbnMgKGV2ZW4gaWYgdGhpcyBpcyBCb25kLCBKYW1lcyBCb25kKSwgSSBob3BlIHRoaXMgcHJvamVjdCBvZmZlcnMgcXVhbGl0eSBlbnRlcnRhaW5tZW50IGFuZCBwbGVhc2luZyB2aXN1YWxpemF0aW9ucy4KCkZyb20gUG9sYW5kIHdpdGggbG92ZSxcCkthY3BlciBEb2Jla1wKaHR0cHM6Ly9naXRodWIuY29tL2thcGlibHVlCgojIyBSZXNvdXJjZXMKCkluc3BpcmF0aW9uOiBodHRwczovL2JldGFhbmRiaXQuZ2l0aHViLmlvL1JNTC8KCkphbWVzIEJvbmQgUiBkYXRhc2V0IGh0dHBzOi8vc2VhcmNoLnItcHJvamVjdC5vcmcvQ1JBTi9yZWZtYW5zL0hvUk0vaHRtbC9KYW1lc0JvbmQuaHRtbAoKSG93IHRvIHVzZSBEQUxFWCB3aXRoIGNhcmV0IGh0dHBzOi8vaHRtbHByZXZpZXcuZ2l0aHViLmlvLz9odHRwczovL2dpdGh1Yi5jb20vTW9kZWxPcmllbnRlZC9EQUxFWC1kb2NzL2Jsb2IvbWFzdGVyL3ZpZ25ldHRlcy9EQUxFWF9jYXJldC5odG1sCgpSIE1hcmtkb3duIENvb2tib29rIGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL3JtYXJrZG93bi1jb29rYm9vay8KCkVNQSBib29rIGh0dHBzOi8vZW1hLmRyd2h5LmFpLwoKRGFuaWVsIENyYWlnJ3MgcmV0cm9zcGVjdGl2ZSBodHRwczovL3lvdXR1LmJlLzJvWmRKcnBoM1JBCgo=