Fast acrobatic maneuvers enable arboreal spiders to hunt dangerous prey
Authors:
Alfonso Aceves-Aparicio*1, 2, Ajay Narendra1, Donald James McLean1, Elizabeth C. Lowe1, Marcelo Christian2, Jonas O. Wolff1, Jutta M. Schneider2 and Marie E. Herberstein1
1, Department of Biological Sciences, Macquarie University, Sydney, NSW, 2109, Australia
2, Institute of Zoology, Universität Hamburg, Hamburg, Germany Corresponding author: Alfonso Aceves-Aparicio Correspondence: bioarach@gmail.com
This is the code used to extract velocity data and generate figures from the attack sequences recorded as high speed video.
library(trajr)
library(tidyverse)
library(readxl)
library(kableExtra)
library(patchwork)
library(plotrix)
library(sjPlot)
source("./R/functions.R")
Data description
Each high speed video was inspected in matlab to track the movement of the ant approaching the ant-slayer spider and the first phase of the capture. Here we have processed the movement of the spiders abdomen during the captures, as this engages earlier during the capture sequence. From each recorded high speed video, a file containing the cartesian coordinates was generated. Each coordinates file is accompanied by a scale file including the coordinates for a known distance. This distance is included in the name of the scale file.
The video sequences were visually scored to identify the precise start of each stage during the strike phase. This information is stored in a spreadsheet containing the video ID, and the frame number for the onset of each stage.
Frames and stage’s description: frame1, first spider movements breaking the “sit and wait” position. frame2, deploying and directing silk from the tree trunk attachment point to the ant. frame3, tagging the prey: this is the moment where the spider attaches the silk line to the ant prey. frame4, free fall defense. frame5, the spider re-settles on the trunk’s surface
Summary: elapsed time
videos_page1 <- read_excel("./Data/velocity_5seq_20200219a.xlsx", col_names = T,
sheet = "velocity_5seq_20200219", range = "A1:AB23")%>%
rename("filename" = "f-name") %>%
mutate(filename = str_replace(filename, ".$", "")) %>%
rename("contact" = Seq3...15) %>%
mutate(firstMoveFrame = frame1 - posFile_start)
# View(videos_page1)
# current A1:AB23
# alternative range: A1:J23
# videos_page1 %>% View()
orientation <- read_csv(file = "./Data/orientation.csv") %>%
mutate(name = str_replace(name, ".$", "")) %>%
rename(filename = "name")
videos_page1 <- left_join(videos_page1, orientation,
by = "filename")
Elapsed time between the critical stages during the ant-slayers’ strike. Average elapsed time and SEM (standard error of the mean) included in the high speed frames shown in Figure 1-B.
# Videos were recorded at 250 frames per second, thus each frame is equal to 0.004 seconds or 4 milliseconds.
elapsed_time_sequences <- videos_page1 %>%
select(1, 5:10) %>%
# slice(- c(3)) %>%
mutate(
# frames are multiply by 0.004 to scale as seconds
seq_1_2 = (frame2-frame1)*0.004,
seq_2_3 = (frame3-frame1)*0.004,
seq_3_4 = (frame4-frame1)*0.004,
seq_4_5 = (frame5-frame1)*0.004)
elapsed_time_sequences %>%
summarise(Mean_1_2 = mean(seq_1_2),
SEM_1_2 = std.error(seq_1_2),
Mean_2_3 = mean(seq_2_3),
SEM_2_3 = std.error(seq_2_3),
Mean_3_4 = mean(seq_3_4),
SEM_3_4 = std.error(seq_3_4),
Mean_4_5 = mean(seq_4_5),
SEM_4_5 = std.error(seq_4_5)) %>%
kableExtra::kbl() %>% kable_styling(full_width = F, html_font = "Times")
Mean_1_2
|
SEM_1_2
|
Mean_2_3
|
SEM_2_3
|
Mean_3_4
|
SEM_3_4
|
Mean_4_5
|
SEM_4_5
|
0.0474545
|
0.0027134
|
0.0865455
|
0.0040306
|
0.1610909
|
0.0103921
|
0.3205455
|
0.0365218
|
View data
elapsed_time_sequences %>% kableExtra::kbl() %>% kable_styling(full_width = F, html_font = "Times", c("striped", "hover"))
filename
|
posFile_start
|
frame1
|
frame2
|
frame3
|
frame4
|
frame5
|
seq_1_2
|
seq_2_3
|
seq_3_4
|
seq_4_5
|
20180728_250fps_spider1_antattack_1
|
86
|
136
|
151
|
163
|
192
|
355
|
0.060
|
0.108
|
0.224
|
0.876
|
20180728_250fps_spider2_antattack_2
|
200
|
250
|
269
|
279
|
305
|
413
|
0.076
|
0.116
|
0.220
|
0.652
|
20180728_250fps_spider3_antattack_3_fail
|
107
|
157
|
171
|
190
|
200
|
223
|
0.056
|
0.132
|
0.172
|
0.264
|
20180728_250fps_spider3_antattack_4
|
349
|
399
|
410
|
419
|
434
|
476
|
0.044
|
0.080
|
0.140
|
0.308
|
20180730_250fps_spider1_antattack_1
|
440
|
490
|
505
|
515
|
527
|
565
|
0.060
|
0.100
|
0.148
|
0.300
|
20180730_250fps_spider2_antattack_3
|
83
|
133
|
147
|
155
|
196
|
250
|
0.056
|
0.088
|
0.252
|
0.468
|
20180730_250fps_spider3_antattack_4
|
209
|
259
|
274
|
285
|
307
|
329
|
0.060
|
0.104
|
0.192
|
0.280
|
20180730_250fps_spider4_antattack_4
|
264
|
314
|
329
|
340
|
366
|
404
|
0.060
|
0.104
|
0.208
|
0.360
|
vid_2019-04-16_19-23-58
|
42
|
92
|
101
|
112
|
124
|
206
|
0.036
|
0.080
|
0.128
|
0.456
|
vid_2019-04-16_19-37-53
|
80
|
130
|
145
|
152
|
198
|
221
|
0.060
|
0.088
|
0.272
|
0.364
|
vid_2019-04-16_20-06-06
|
32
|
82
|
92
|
101
|
113
|
130
|
0.040
|
0.076
|
0.124
|
0.192
|
vid_2019-04-16_20-17-59
|
117
|
167
|
181
|
190
|
197
|
225
|
0.056
|
0.092
|
0.120
|
0.232
|
vid_2019-04-16_20-23-15
|
0
|
34
|
46
|
54
|
63
|
75
|
0.048
|
0.080
|
0.116
|
0.164
|
vid_2019-04-16_20-39-56
|
25
|
75
|
84
|
95
|
123
|
155
|
0.036
|
0.080
|
0.192
|
0.320
|
vid_2019-04-16_21-12-25
|
32
|
82
|
89
|
95
|
106
|
122
|
0.028
|
0.052
|
0.096
|
0.160
|
vid_2019-04-17_19-06-01
|
57
|
107
|
119
|
130
|
148
|
178
|
0.048
|
0.092
|
0.164
|
0.284
|
vid_2019-04-17_19-12-02
|
47
|
97
|
107
|
114
|
129
|
182
|
0.040
|
0.068
|
0.128
|
0.340
|
vid_2019-04-17_19-22-12
|
96
|
146
|
157
|
167
|
178
|
191
|
0.044
|
0.084
|
0.128
|
0.180
|
vid_2019-04-17_19-49-08
|
54
|
104
|
114
|
122
|
132
|
144
|
0.040
|
0.072
|
0.112
|
0.160
|
vid_2019-04-17_20-06-08
|
0
|
45
|
52
|
66
|
79
|
113
|
0.028
|
0.084
|
0.136
|
0.272
|
vid_2019-04-17_20-49-15
|
8
|
58
|
67
|
73
|
90
|
113
|
0.036
|
0.060
|
0.128
|
0.220
|
vid_2019-04-17_20-53-42
|
20
|
70
|
78
|
86
|
106
|
120
|
0.032
|
0.064
|
0.144
|
0.200
|
Processing of cartesian coordinates
The individual data files for the video sequences are input in the order shown in the excel spread sheet by matching the file names. The scale recorded in the file names is also used to process the data prior to its analysis.
# Grab the names of all the files in the working directory as set above
all_pos <- list.files("./Data2", pattern =".pos", full.names = TRUE)
# Grabs the names of the video files from the control spreadsheet
# note the current directory only contains videos from the chronos camera
input_list <- videos_page1 %>%
select("filename") %>%
pull()
# input_list
# creates a single object to be used as a pattern to grab the pos files that match those in the spreadsheet
pattern <- paste(input_list, sep="", collapse="|")
# list of pos files that match names in the spreadsheets
chosen_pos <- all_pos[grepl(pattern, all_pos)]
# first containers --------------------------------------------------------
# coord_files <- chosen_pos %>%
# str_subset(pattern = "-(\\d){2}.pos$")
coord_files <- chosen_pos %>%
str_subset(pattern = "(\\d|\\d_fail)\\.pos$")
# scale_files <- chosen_pos %>%
# str_subset(pattern = "_(\\d){2}mm.pos$")
scale_files <- chosen_pos %>%
str_subset(pattern = "(\\d){1,}_scale_(\\d){1,}(mm)|(\\d){1,}_fail_scale_(\\d){1,}(mm)\\.pos$")
# extract the known distances from the the names of the scale files
the_scales <- scale_files %>%
as.data.frame() %>%
rename("scaleFiles" = ".") %>%
mutate(scaleValues = str_extract(scaleFiles, pattern = "_(\\d){1,2}mm")) %>%
mutate(scaleValues = str_extract(string = scaleValues, pattern = "_\\d+")) %>%
mutate(scaleValues = str_replace(scaleValues, pattern = "_", replacement = "0.0")) %>%
mutate(scaleValues = as.numeric(scaleValues))
Assessing attack speed
Trajectories from videos
We used the R package “Trajr” to generate the trajectories (the path of the moving spiders over time) followed by the spiders during the duration of the attack strike against the ant prey.
Each trajectory was smoothed by applying a Savitzky-Golay to reduce high frequency noise while preserving the shape of the trajectory (function: TrajSmoothSG). Further, we used the TrajDerivatives function to calculate change in speed along a each trajectory. The outcome is stored in a “derivatives” object.
This data is used in conjunction with the visually scored onset of the attack stages to calculate the maximum speed reached by the spiders during their somersault maneuvers.
Trajr:
McLean DJ, Skowron Volponi MA. trajr: An R package for characterisation of animal trajectories. Ethology. 2018;00:1–9. https://doi.org/10.1111/eth.12739.
https://cran.rstudio.com/web/packages/trajr/vignettes/trajr-vignette.html
Cartesian coordinates to speed data
Steps to prepare the cartesian data for speed analysis.
Trajectories are created and stored in the list object “all_trajs” (22). Each one is name sequentially and corresponding to the file order in the data frame videos_page1.
# this line creates a list containing all the elements to create each trajectory object
trajs_list <- list(coords = coord_files, scales = scale_files, scaleValues = the_scales)
all_trajs <- map(pluck(trajs_list, 1) %>% seq_along() ,~ trajs_list %>% loadSample_FromList(.x)) %>%
set_names(mylabelseq("Traj_", 1, 22, 1))
# all_trajs %>% glimpse()
Create derivatives objects. Each derivative object contains the calculated speed and change in speed along each trajectory. These are stored in the list object: derivs_list
derivs_list <- pmap(list(coord_files, scale_files, the_scales$scaleValues), loadSample_FromList2) %>%
# smoothing parameters passed to TrajSmootSG junction
map(TrajSmoothSG, p = 5, n = 23) %>%
map(TrajDerivatives) %>%
# each derivatives object is labelled sequentially with the prefix: Derivs
set_names(mylabelseq("Derivs_", 1, 22, 1))
derivs_names <- names(derivs_list)
Frames 1 to 5 indicate the start of each stage during the strike phase of the ant slayer’s attack. The first move (attack onset) is recorded as “frame1”. To match the frame number at which the attack onset occurs with its index position, the values in “posFile_start” should be subtracted from the frame numbers.
first_move <- as.vector(videos_page1$frame1 - videos_page1$posFile_start)
The first move frame is used to control for any spider movement before the spider started the attack sequence.
derivs_list_0 <- derivs_list
for(i in 1:length(derivs_list_0)) {
derivs_list_0[[i]]$speed[c(1:first_move[i]-1)] <- 0
}
# Trimming of video the end part of video 03
# The last frames in the 3rd video are trimmed here to correct for a mistakenly scored positions
# Trims the derivatives
for(i in 1:length(derivs_list_0$Derivs_03)) {
derivs_list_0$Derivs_03[[i]] <-
derivs_list_0$Derivs_03[[i]][c(1:120)]
}
# Trims the trajectory
all_trajs$Traj_03 <-
all_trajs$Traj_03[1:120, ]
Peak speed frame indexes
Here we calculated the peak speed reached by spiders within a defined time window that covers the strike phase. The result is the index position where the peak speed was reached. Then this frame index can be used to access the time and speed at that moment (see below).
# The "IndexLimitsManual" function pulls the index position of the following frame index position:
# frame where the acrobatic strike reaches its maximum speed
# default values used for the function
# IndexLimitsManual(derivs_object = derivs_list_sample$Derivs_01, startThreshold = 1e-8, thresholdFrame = 1, limit_range = 155, window_size = 35)
indexes_0 <- map(derivs_list_0, IndexLimitsManual) %>% map(~ pluck(., 3) %>% first())
# change name to remove short
Speed & Trajectory
These plots show the speed change during the first phase of the attack (acrobatic strike) and the trajectory followed.
The blue asterisk marks the onset of the spiders’ attack (the spider begins moving). The red asterisk shows where the peak speed was reached during the acrobatic strike. Note this is restricted to the speeds achieved by the spiders while directing its attack to the ant prey and before dropping off from the trunk surface.
par(mfrow = c(2,1))
for(i in 1:length(all_trajs)) {
onset_to_peak_speed <-
c(c(first_move[i]-1), indexes_0[[i]])
par(mfrow = c(1,2));
derivs_list_0[[i]] %>% plotSpeed();
points(
derivs_list_0[[i]]$speed[onset_to_peak_speed] ~
derivs_list_0[[i]]$speedTimes[onset_to_peak_speed],
pch = 8, col = c("blue", "red"))
plot(all_trajs[[i]], lwd = 1, lty = 1)
points(all_trajs[[i]][onset_to_peak_speed, ], pch = 8,col = c("blue", "red"))
title(main = names(all_trajs[i]), outer = T, line = -2)
}






















Speed curves + elapsed time boxplot
Figure 1 Panel F
The figure plots the speed change from the last frame before the acrobatic attack onset to the maximum speed reached for the 22 inspected high speed videos.
# loop me up!
# colour palette
cbp1 <- rev(c("#F0E442", "#999999", "#E69F00", "#56B4E9", "#009E73",
"#0072B2", "#D55E00", "#CC79A7"))
# to recycle colours
cbp1_24 <- rep(cbp1, 3)
mp <- ggplot()
for(i in seq_along(derivs_list_0)) {
onset_to_peak_speed <- c(first_move[i]-1):indexes_0[[i]]
idata <- tibble("speed" = derivs_list_0[[i]]$speed[onset_to_peak_speed]) %>%
mutate("index" = seq(1,length(speed), 1)-1)
mp <- mp + geom_line(data = idata,
aes(x = index, y = speed*100), colour = cbp1_24[i]) +
geom_point(data = idata,
aes(x = last(index),
y = last(speed*100)),
colour = cbp1_24[i]) +
expand_limits(x = c(0, 30), y = c(0, 65)) +
ylab("Speed (cm/s)") +
xlab("Time (ms)") +
theme_classic() +
theme(panel.border = element_blank()) +
scale_x_continuous(labels=function(x)x*4, n.breaks = 6)
}
# print(mp)
# Max speeds for each spider and the elapsed time while going from 0 to to max.
table_speed_duration <-
map_dfr(derivs_list_0, SpeedAndTime2) %>%
mutate(from = derivs_names, .before = max_speed)
# Inset box plot for elapsed time during speed change included in figure 1 panel f
# creates boxplot for elapsed time from 0 to peak
inset2 <- table_speed_duration %>%
ggplot() +
geom_boxplot(aes(elapsed_time)) +
scale_x_continuous(n.breaks = 4) +
expand_limits(x = c(0, 120))
# removes elements from the boxplot to make it an inset in the speed curves panel
inset2 <- inset2 +
theme( legend.position = "none",
panel.grid = element_blank(),
panel.background = element_rect(fill = "transparent",colour = NA),
plot.background = element_rect(fill = "transparent",colour = NA),
axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
# panel.background = element_blank(),
)
Plot
ggp_combi2 <- inset2 / mp +
plot_layout(heights = c(.25, 4))
ggp_combi2

# ggsave(filename = "panelF3.pdf", plot = ggp_combi2, device = "pdf", path = "./gg_plots")
Data tables
Calculated peak speeds for each spider and the elapsed time while going from 0 to to max
table_speed_duration %>% kableExtra::kbl() %>% kable_styling(full_width = F, html_font = "Times", c("striped", "hover"))
from
|
max_speed
|
elapsed_time
|
Derivs_01
|
11.57657
|
84
|
Derivs_02
|
24.35238
|
120
|
Derivs_03
|
18.34887
|
112
|
Derivs_04
|
29.94638
|
68
|
Derivs_05
|
61.83679
|
104
|
Derivs_06
|
19.86896
|
84
|
Derivs_07
|
11.17088
|
56
|
Derivs_08
|
21.52736
|
92
|
Derivs_09
|
31.64278
|
64
|
Derivs_10
|
33.36874
|
104
|
Derivs_11
|
14.53998
|
52
|
Derivs_12
|
32.73323
|
84
|
Derivs_13
|
29.30936
|
64
|
Derivs_14
|
29.20735
|
60
|
Derivs_15
|
15.24193
|
44
|
Derivs_16
|
25.19305
|
68
|
Derivs_17
|
33.06568
|
64
|
Derivs_18
|
25.37552
|
68
|
Derivs_19
|
23.51403
|
68
|
Derivs_20
|
30.45109
|
48
|
Derivs_21
|
22.21806
|
52
|
Derivs_22
|
15.98763
|
68
|
Means and SEM
table_speed_duration %>%
summarise(avg_speed = mean(max_speed),
SEM_speed = std.error(max_speed),
avg_duration = mean(elapsed_time),
SEM_duration = std.error(elapsed_time)) %>% kableExtra::kbl() %>% kable_styling(full_width = F, html_font = "Times", c("striped", "hover"))
avg_speed
|
SEM_speed
|
avg_duration
|
SEM_duration
|
25.47621
|
2.294305
|
74
|
4.541297
|
# round(mean(table_speed_duration$max_speed), 2)
# round(mean(table_speed_duration$elapsed_time), 2)
Median and frequencies table for the elapsed time (ms)
median(table_speed_duration$elapsed_time)
## [1] 68
table(table_speed_duration$elapsed_time)
##
## 44 48 52 56 60 64 68 84 92 104 112 120
## 1 1 2 1 1 3 5 3 1 2 1 1
Supplementary
We explored the effect of spider size and orientation of the attack (measured as the angle towards which the spider directed the somersault attack).
Size and orientation
Model summary
# creating dataset
size_speed_orientation <- bind_cols(
table_speed_duration,
videos_page1 %>%
select(`spider size`, Orientation)
)
# the model GLM
glm_size <- glm(max_speed ~ `spider size`, family = Gamma(link = "log"),
data = size_speed_orientation # %>% filter(max_speeds_all < 60)
)
# Null model
glm_null <- glm(max_speed ~ 1, family = Gamma(link = "log"),
data = size_speed_orientation)
# summary
summary(glm_size)
##
## Call:
## glm(formula = max_speed ~ `spider size`, family = Gamma(link = "log"),
## data = size_speed_orientation)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -0.6261 -0.2619 -0.0053 0.1289 0.9737
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.66346 0.30642 8.692 3.16e-08 ***
## `spider size` 0.13555 0.07099 1.909 0.0707 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for Gamma family taken to be 0.1628189)
##
## Null deviance: 3.3488 on 21 degrees of freedom
## Residual deviance: 2.7870 on 20 degrees of freedom
## AIC: 162.56
##
## Number of Fisher Scoring iterations: 4
Explained deviance
anova(glm_size, glm_null, test = "Chi")
# no significant difference with the null model
mod_dev <- round(100*(glm_size$null.deviance - glm_size$deviance)/glm_size$null.deviance, 2)
mod_dev
## [1] 16.78
# 16.78 %
speed_size_plot <- size_speed_orientation %>%
ggplot() +
geom_point(aes(x = `spider size`, y = max_speed)) +
stat_smooth(aes(x = `spider size`, y = max_speed),
method = "glm",
formula = y~I(1/x),
method.args = list(family = Gamma(link = "log")),
se = T) +
labs(x = "Spider body length (mm)", y = "Peak speed cm/s") +
theme_classic()
# speed_size_plot
Orientation
We tested the association between linear (maximum speed) and circular (attack orientation) variables with the Johnson–Wehrly–Mardia correlation coefficient (see chapter 8 in reference below). Where R2𝑥𝜃 ranges between zero and one. Values closer to one refer to stronger associations. Spiders did not exhibit any preference for the direction in which they attacked the prey
Pewsey A, Neuhäuser M, Ruxton GD. Circular statistics in R. Oxford University Press; 2013.
# converting degrees to radians
withRad <- size_speed_orientation %>%
mutate(OriRad2 = Orientation*2*pi/360)
R2xtIndTestRand(withRad$max_speed,
withRad$OriRad2, 9999)
## coefficient p-value
## 0.1289073 0.2744000
direction_plot <- size_speed_orientation %>%
ggplot() +
geom_point(aes(x = abs(Orientation), y = max_speed)) +
labs(x = "Attack direction (degrees)", y = "Peak speed cm/s") +
theme_classic()
# direction_plot
Supplementary plot
speed_size_plot + direction_plot + plot_layout(ncol = 1)

# setwd("/Users/alfonsoaceves/Downloads/Ant Slayer Speed/gg_plots")
# write_csv(speeds_times, "speeds_times.csv")
LS0tCnRpdGxlOiAiQW50IFNsYXllciIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2ZvbGRpbmc6ICJoaWRlIgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKICBtYXJrZG93bjogCiAgICB3cmFwOiBzZW50ZW5jZQotLS0KCiMgRmFzdCBhY3JvYmF0aWMgbWFuZXV2ZXJzIGVuYWJsZSBhcmJvcmVhbCBzcGlkZXJzIHRvIGh1bnQgZGFuZ2Vyb3VzIHByZXkKCioqQXV0aG9yczoqKgoKQWxmb25zbyBBY2V2ZXMtQXBhcmljaW9cKl4xLMKgMl4sIEFqYXkgTmFyZW5kcmFeMV4sIERvbmFsZCBKYW1lcyBNY0xlYW5eMV4sIEVsaXphYmV0aCBDLiBMb3dlXjFeLCBNYXJjZWxvIENocmlzdGlhbl4yXiwgSm9uYXMgTy4gV29sZmZeMV4sIEp1dHRhIE0uIFNjaG5laWRlcl4yXiBhbmQgTWFyaWUgRS4gSGVyYmVyc3RlaW5eMV4KCjEsIERlcGFydG1lbnQgb2YgQmlvbG9naWNhbCBTY2llbmNlcywgTWFjcXVhcmllIFVuaXZlcnNpdHksIFN5ZG5leSwgTlNXLCAyMTA5LCBBdXN0cmFsaWEKCjIsIEluc3RpdHV0ZSBvZiBab29sb2d5LCBVbml2ZXJzaXTDpHQgSGFtYnVyZywgSGFtYnVyZywgR2VybWFueSBDb3JyZXNwb25kaW5nIGF1dGhvcjogQWxmb25zbyBBY2V2ZXMtQXBhcmljaW8gQ29ycmVzcG9uZGVuY2U6IFtiaW9hcmFjaFxAZ21haWwuY29tXShtYWlsdG86YmlvYXJhY2hAZ21haWwuY29tKXsuZW1haWx9CgpUaGlzIGlzIHRoZSBjb2RlIHVzZWQgdG8gZXh0cmFjdCB2ZWxvY2l0eSBkYXRhIGFuZCBnZW5lcmF0ZSBmaWd1cmVzIGZyb20gdGhlIGF0dGFjayBzZXF1ZW5jZXMgcmVjb3JkZWQgYXMgaGlnaCBzcGVlZCB2aWRlby4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodHJhanIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShwbG90cml4KQpsaWJyYXJ5KHNqUGxvdCkKCnNvdXJjZSgiLi9SL2Z1bmN0aW9ucy5SIikKCmBgYAoKIyMgRGF0YSBkZXNjcmlwdGlvbiB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30KCkVhY2ggaGlnaCBzcGVlZCB2aWRlbyB3YXMgaW5zcGVjdGVkIGluIG1hdGxhYiB0byB0cmFjayB0aGUgbW92ZW1lbnQgb2YgdGhlIGFudCBhcHByb2FjaGluZyB0aGUgYW50LXNsYXllciBzcGlkZXIgYW5kIHRoZSBmaXJzdCBwaGFzZSBvZiB0aGUgY2FwdHVyZS4KSGVyZSB3ZSBoYXZlIHByb2Nlc3NlZCB0aGUgbW92ZW1lbnQgb2YgdGhlIHNwaWRlcnMgYWJkb21lbiBkdXJpbmcgdGhlIGNhcHR1cmVzLCBhcyB0aGlzIGVuZ2FnZXMgZWFybGllciBkdXJpbmcgdGhlIGNhcHR1cmUgc2VxdWVuY2UuCkZyb20gZWFjaCByZWNvcmRlZCBoaWdoIHNwZWVkIHZpZGVvLCBhIGZpbGUgY29udGFpbmluZyB0aGUgY2FydGVzaWFuIGNvb3JkaW5hdGVzIHdhcyBnZW5lcmF0ZWQuCkVhY2ggY29vcmRpbmF0ZXMgZmlsZSBpcyBhY2NvbXBhbmllZCBieSBhIHNjYWxlIGZpbGUgaW5jbHVkaW5nIHRoZSBjb29yZGluYXRlcyBmb3IgYSBrbm93biBkaXN0YW5jZS4KVGhpcyBkaXN0YW5jZSBpcyBpbmNsdWRlZCBpbiB0aGUgbmFtZSBvZiB0aGUgc2NhbGUgZmlsZS4KClRoZSB2aWRlbyBzZXF1ZW5jZXMgd2VyZSB2aXN1YWxseSBzY29yZWQgdG8gaWRlbnRpZnkgdGhlIHByZWNpc2Ugc3RhcnQgb2YgZWFjaCBzdGFnZSBkdXJpbmcgdGhlIHN0cmlrZSBwaGFzZS4KVGhpcyBpbmZvcm1hdGlvbiBpcyBzdG9yZWQgaW4gYSBzcHJlYWRzaGVldCBjb250YWluaW5nIHRoZSB2aWRlbyBJRCwgYW5kIHRoZSBmcmFtZSBudW1iZXIgZm9yIHRoZSBvbnNldCBvZiBlYWNoIHN0YWdlLgoKRnJhbWVzIGFuZCBzdGFnZSdzIGRlc2NyaXB0aW9uOiBmcmFtZTEsIGZpcnN0IHNwaWRlciBtb3ZlbWVudHMgYnJlYWtpbmcgdGhlICJzaXQgYW5kIHdhaXQiIHBvc2l0aW9uLgpmcmFtZTIsIGRlcGxveWluZyBhbmQgZGlyZWN0aW5nIHNpbGsgZnJvbSB0aGUgdHJlZSB0cnVuayBhdHRhY2htZW50IHBvaW50IHRvIHRoZSBhbnQuCmZyYW1lMywgdGFnZ2luZyB0aGUgcHJleTogdGhpcyBpcyB0aGUgbW9tZW50IHdoZXJlIHRoZSBzcGlkZXIgYXR0YWNoZXMgdGhlIHNpbGsgbGluZSB0byB0aGUgYW50IHByZXkuCmZyYW1lNCwgZnJlZSBmYWxsIGRlZmVuc2UuCmZyYW1lNSwgdGhlIHNwaWRlciByZS1zZXR0bGVzIG9uIHRoZSB0cnVuaydzIHN1cmZhY2UKCgojIyMgU3VtbWFyeTogZWxhcHNlZCB0aW1lCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KdmlkZW9zX3BhZ2UxIDwtIHJlYWRfZXhjZWwoIi4vRGF0YS92ZWxvY2l0eV81c2VxXzIwMjAwMjE5YS54bHN4IiwgY29sX25hbWVzID0gVCwgCnNoZWV0ID0gInZlbG9jaXR5XzVzZXFfMjAyMDAyMTkiLCByYW5nZSA9ICJBMTpBQjIzIiklPiUgCiAgcmVuYW1lKCJmaWxlbmFtZSIgPSAiZi1uYW1lIikgJT4lIAogIG11dGF0ZShmaWxlbmFtZSA9IHN0cl9yZXBsYWNlKGZpbGVuYW1lLCAiLiQiLCAiIikpICU+JSAKICByZW5hbWUoImNvbnRhY3QiID0gU2VxMy4uLjE1KSAlPiUKICBtdXRhdGUoZmlyc3RNb3ZlRnJhbWUgPSBmcmFtZTEgLSBwb3NGaWxlX3N0YXJ0KQojIFZpZXcodmlkZW9zX3BhZ2UxKQojIGN1cnJlbnQgQTE6QUIyMwojIGFsdGVybmF0aXZlIHJhbmdlOiBBMTpKMjMKIyB2aWRlb3NfcGFnZTEgJT4lIFZpZXcoKQoKb3JpZW50YXRpb24gPC0gcmVhZF9jc3YoZmlsZSA9ICIuL0RhdGEvb3JpZW50YXRpb24uY3N2IikgJT4lIAogIG11dGF0ZShuYW1lID0gc3RyX3JlcGxhY2UobmFtZSwgIi4kIiwgIiIpKSAlPiUgCiAgcmVuYW1lKGZpbGVuYW1lID0gIm5hbWUiKQoKdmlkZW9zX3BhZ2UxIDwtIGxlZnRfam9pbih2aWRlb3NfcGFnZTEsIG9yaWVudGF0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJmaWxlbmFtZSIpCmBgYAoKCgpFbGFwc2VkIHRpbWUgYmV0d2VlbiB0aGUgY3JpdGljYWwgc3RhZ2VzIGR1cmluZyB0aGUgYW50LXNsYXllcnMnIHN0cmlrZS4KQXZlcmFnZSBlbGFwc2VkIHRpbWUgYW5kIFNFTSAoc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1lYW4pIGluY2x1ZGVkIGluIHRoZSBoaWdoIHNwZWVkIGZyYW1lcyBzaG93biBpbiBGaWd1cmUgMS1CLgoKCmBgYHtyfQojIFZpZGVvcyB3ZXJlIHJlY29yZGVkIGF0IDI1MCBmcmFtZXMgcGVyIHNlY29uZCwgdGh1cyBlYWNoIGZyYW1lIGlzIGVxdWFsIHRvIDAuMDA0IHNlY29uZHMgb3IgNCBtaWxsaXNlY29uZHMuCgplbGFwc2VkX3RpbWVfc2VxdWVuY2VzIDwtIHZpZGVvc19wYWdlMSAlPiUgCiAgc2VsZWN0KDEsIDU6MTApICU+JSAKICAjIHNsaWNlKC0gYygzKSkgJT4lIAogIG11dGF0ZSgKICAgICMgZnJhbWVzIGFyZSBtdWx0aXBseSBieSAwLjAwNCB0byBzY2FsZSBhcyBzZWNvbmRzCiAgICBzZXFfMV8yID0gKGZyYW1lMi1mcmFtZTEpKjAuMDA0LAogICAgc2VxXzJfMyA9IChmcmFtZTMtZnJhbWUxKSowLjAwNCwKICAgIHNlcV8zXzQgPSAoZnJhbWU0LWZyYW1lMSkqMC4wMDQsCiAgICBzZXFfNF81ID0gKGZyYW1lNS1mcmFtZTEpKjAuMDA0KQoKCmBgYAoKCgpgYGB7cn0KZWxhcHNlZF90aW1lX3NlcXVlbmNlcyAlPiUgCiAgc3VtbWFyaXNlKE1lYW5fMV8yID0gbWVhbihzZXFfMV8yKSwKICAgICAgICAgICAgU0VNXzFfMiA9IHN0ZC5lcnJvcihzZXFfMV8yKSwKICAgICAgICAgICAgTWVhbl8yXzMgPSBtZWFuKHNlcV8yXzMpLAogICAgICAgICAgICBTRU1fMl8zID0gc3RkLmVycm9yKHNlcV8yXzMpLAogICAgICAgICAgICBNZWFuXzNfNCA9IG1lYW4oc2VxXzNfNCksCiAgICAgICAgICAgIFNFTV8zXzQgPSBzdGQuZXJyb3Ioc2VxXzNfNCksCiAgICAgICAgICAgIE1lYW5fNF81ID0gbWVhbihzZXFfNF81KSwKICAgICAgICAgICAgU0VNXzRfNSA9IHN0ZC5lcnJvcihzZXFfNF81KSkgJT4lCiAga2FibGVFeHRyYTo6a2JsKCkgJT4lIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYsIGh0bWxfZm9udCA9ICJUaW1lcyIpCgpgYGAKCjxicj4KCiMjIyBWaWV3IGRhdGEKYGBge3J9CgplbGFwc2VkX3RpbWVfc2VxdWVuY2VzICU+JSBrYWJsZUV4dHJhOjprYmwoKSAlPiUga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgaHRtbF9mb250ID0gIlRpbWVzIiwgYygic3RyaXBlZCIsICJob3ZlciIpKQoKYGBgCgo8YnI+CgojIyMgUHJvY2Vzc2luZyBvZiBjYXJ0ZXNpYW4gY29vcmRpbmF0ZXMKClRoZSBpbmRpdmlkdWFsIGRhdGEgZmlsZXMgZm9yIHRoZSB2aWRlbyBzZXF1ZW5jZXMgYXJlIGlucHV0IGluIHRoZSBvcmRlciBzaG93biBpbiB0aGUgZXhjZWwgc3ByZWFkIHNoZWV0IGJ5IG1hdGNoaW5nIHRoZSBmaWxlIG5hbWVzLiBUaGUgc2NhbGUgcmVjb3JkZWQgaW4gdGhlIGZpbGUgbmFtZXMgaXMgYWxzbyB1c2VkIHRvIHByb2Nlc3MgdGhlIGRhdGEgcHJpb3IgdG8gaXRzIGFuYWx5c2lzLgoKYGBge3J9CiMgR3JhYiB0aGUgbmFtZXMgb2YgYWxsIHRoZSBmaWxlcyBpbiB0aGUgd29ya2luZyBkaXJlY3RvcnkgYXMgc2V0IGFib3ZlCgphbGxfcG9zIDwtIGxpc3QuZmlsZXMoIi4vRGF0YTIiLCBwYXR0ZXJuID0iLnBvcyIsIGZ1bGwubmFtZXMgPSBUUlVFKQoKIyBHcmFicyB0aGUgbmFtZXMgb2YgdGhlIHZpZGVvIGZpbGVzIGZyb20gdGhlIGNvbnRyb2wgc3ByZWFkc2hlZXQKIyBub3RlIHRoZSBjdXJyZW50IGRpcmVjdG9yeSBvbmx5IGNvbnRhaW5zIHZpZGVvcyBmcm9tIHRoZSBjaHJvbm9zIGNhbWVyYQppbnB1dF9saXN0IDwtIHZpZGVvc19wYWdlMSAlPiUgCiAgc2VsZWN0KCJmaWxlbmFtZSIpICU+JSAKICBwdWxsKCkKCiMgaW5wdXRfbGlzdAoKIyBjcmVhdGVzIGEgc2luZ2xlIG9iamVjdCB0byBiZSB1c2VkIGFzIGEgcGF0dGVybiB0byBncmFiIHRoZSBwb3MgZmlsZXMgdGhhdCBtYXRjaCB0aG9zZSBpbiB0aGUgc3ByZWFkc2hlZXQKcGF0dGVybiA8LSBwYXN0ZShpbnB1dF9saXN0LCBzZXA9IiIsIGNvbGxhcHNlPSJ8IikKCiMgbGlzdCBvZiBwb3MgZmlsZXMgdGhhdCBtYXRjaCBuYW1lcyBpbiB0aGUgc3ByZWFkc2hlZXRzCmNob3Nlbl9wb3MgPC0gYWxsX3Bvc1tncmVwbChwYXR0ZXJuLCBhbGxfcG9zKV0KYGBgCgoKCmBgYHtyfQojIGZpcnN0IGNvbnRhaW5lcnMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgY29vcmRfZmlsZXMgPC0gY2hvc2VuX3BvcyAlPiUgCiAgIyBzdHJfc3Vic2V0KHBhdHRlcm4gPSAiLShcXGQpezJ9LnBvcyQiKQoKY29vcmRfZmlsZXMgPC0gY2hvc2VuX3BvcyAlPiUgCiAgc3RyX3N1YnNldChwYXR0ZXJuID0gIihcXGR8XFxkX2ZhaWwpXFwucG9zJCIpCgojIHNjYWxlX2ZpbGVzIDwtIGNob3Nlbl9wb3MgJT4lIAogICMgc3RyX3N1YnNldChwYXR0ZXJuID0gIl8oXFxkKXsyfW1tLnBvcyQiKQoKc2NhbGVfZmlsZXMgPC0gY2hvc2VuX3BvcyAlPiUgCiAgc3RyX3N1YnNldChwYXR0ZXJuID0gIihcXGQpezEsfV9zY2FsZV8oXFxkKXsxLH0obW0pfChcXGQpezEsfV9mYWlsX3NjYWxlXyhcXGQpezEsfShtbSlcXC5wb3MkIikKCiMgZXh0cmFjdCB0aGUga25vd24gZGlzdGFuY2VzIGZyb20gdGhlIHRoZSBuYW1lcyBvZiB0aGUgc2NhbGUgZmlsZXMKdGhlX3NjYWxlcyA8LSBzY2FsZV9maWxlcyAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JSAKICByZW5hbWUoInNjYWxlRmlsZXMiID0gIi4iKSAlPiUgCiAgbXV0YXRlKHNjYWxlVmFsdWVzID0gIHN0cl9leHRyYWN0KHNjYWxlRmlsZXMsIHBhdHRlcm4gPSAiXyhcXGQpezEsMn1tbSIpKSAlPiUgCiAgbXV0YXRlKHNjYWxlVmFsdWVzID0gIHN0cl9leHRyYWN0KHN0cmluZyA9IHNjYWxlVmFsdWVzLCBwYXR0ZXJuID0gIl9cXGQrIikpICU+JSAKICBtdXRhdGUoc2NhbGVWYWx1ZXMgPSBzdHJfcmVwbGFjZShzY2FsZVZhbHVlcywgcGF0dGVybiA9ICJfIiwgcmVwbGFjZW1lbnQgPSAiMC4wIikpICU+JSAKICBtdXRhdGUoc2NhbGVWYWx1ZXMgPSBhcy5udW1lcmljKHNjYWxlVmFsdWVzKSkKYGBgCgo8YnI+CgojIyBBc3Nlc3NpbmcgYXR0YWNrIHNwZWVkIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQoKIyMjIFRyYWplY3RvcmllcyBmcm9tIHZpZGVvcyAKCgpXZSB1c2VkIHRoZSBSIHBhY2thZ2UgIlRyYWpyIiB0byBnZW5lcmF0ZSB0aGUgdHJhamVjdG9yaWVzICh0aGUgcGF0aCBvZiB0aGUgbW92aW5nIHNwaWRlcnMgb3ZlciB0aW1lKSBmb2xsb3dlZCBieSB0aGUgc3BpZGVycyBkdXJpbmcgdGhlIGR1cmF0aW9uIG9mIHRoZSBhdHRhY2sgc3RyaWtlIGFnYWluc3QgdGhlIGFudCBwcmV5LgoKRWFjaCB0cmFqZWN0b3J5IHdhcyBzbW9vdGhlZCBieSBhcHBseWluZyBhIFNhdml0emt5LUdvbGF5IHRvIHJlZHVjZSBoaWdoIGZyZXF1ZW5jeSBub2lzZSB3aGlsZSBwcmVzZXJ2aW5nIHRoZSBzaGFwZSBvZiB0aGUgdHJhamVjdG9yeSAoZnVuY3Rpb246IFRyYWpTbW9vdGhTRykuCkZ1cnRoZXIsIHdlIHVzZWQgdGhlIFRyYWpEZXJpdmF0aXZlcyBmdW5jdGlvbiB0byBjYWxjdWxhdGUgY2hhbmdlIGluIHNwZWVkIGFsb25nIGEgZWFjaCB0cmFqZWN0b3J5LgpUaGUgb3V0Y29tZSBpcyBzdG9yZWQgaW4gYSAiZGVyaXZhdGl2ZXMiIG9iamVjdC4KClRoaXMgZGF0YSBpcyB1c2VkIGluIGNvbmp1bmN0aW9uIHdpdGggdGhlIHZpc3VhbGx5IHNjb3JlZCBvbnNldCBvZiB0aGUgYXR0YWNrIHN0YWdlcyB0byBjYWxjdWxhdGUgdGhlIG1heGltdW0gc3BlZWQgcmVhY2hlZCBieSB0aGUgc3BpZGVycyBkdXJpbmcgdGhlaXIgc29tZXJzYXVsdCBtYW5ldXZlcnMuCgpUcmFqcjoKCk1jTGVhbiBESiwgU2tvd3JvbiBWb2xwb25pIE1BLgp0cmFqcjogQW4gUiBwYWNrYWdlIGZvciBjaGFyYWN0ZXJpc2F0aW9uIG9mIGFuaW1hbCB0cmFqZWN0b3JpZXMuCkV0aG9sb2d5LgoyMDE4OzAwOjEtLTkuCjxodHRwczovL2RvaS5vcmcvMTAuMTExMS9ldGguMTI3MzkuPgoKPGh0dHBzOi8vY3Jhbi5yc3R1ZGlvLmNvbS93ZWIvcGFja2FnZXMvdHJhanIvdmlnbmV0dGVzL3RyYWpyLXZpZ25ldHRlLmh0bWw+Cgo8YnI+CgojIyMgQ2FydGVzaWFuIGNvb3JkaW5hdGVzIHRvIHNwZWVkIGRhdGEKCioqU3RlcHMgdG8gcHJlcGFyZSB0aGUgY2FydGVzaWFuIGRhdGEgZm9yIHNwZWVkIGFuYWx5c2lzLioqCgpUcmFqZWN0b3JpZXMgYXJlIGNyZWF0ZWQgYW5kIHN0b3JlZCBpbiB0aGUgbGlzdCBvYmplY3QgImFsbF90cmFqcyIgKDIyKS4KRWFjaCBvbmUgaXMgbmFtZSBzZXF1ZW50aWFsbHkgYW5kIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGZpbGUgb3JkZXIgaW4gdGhlIGRhdGEgZnJhbWUgdmlkZW9zX3BhZ2UxLgoKYGBge3IgZWNobz1UUlVFfQojIHRoaXMgbGluZSBjcmVhdGVzIGEgbGlzdCBjb250YWluaW5nIGFsbCB0aGUgZWxlbWVudHMgdG8gY3JlYXRlIGVhY2ggdHJhamVjdG9yeSBvYmplY3QKdHJhanNfbGlzdCA8LSBsaXN0KGNvb3JkcyA9IGNvb3JkX2ZpbGVzLCBzY2FsZXMgPSBzY2FsZV9maWxlcywgc2NhbGVWYWx1ZXMgPSB0aGVfc2NhbGVzKQoKYWxsX3RyYWpzIDwtIG1hcChwbHVjayh0cmFqc19saXN0LCAxKSAlPiUgc2VxX2Fsb25nKCkgLH4gdHJhanNfbGlzdCAlPiUgbG9hZFNhbXBsZV9Gcm9tTGlzdCgueCkpICU+JSAKICBzZXRfbmFtZXMobXlsYWJlbHNlcSgiVHJhal8iLCAxLCAyMiwgMSkpCgojIGFsbF90cmFqcyAlPiUgZ2xpbXBzZSgpCmBgYAoKCkNyZWF0ZSBkZXJpdmF0aXZlcyBvYmplY3RzLiBFYWNoIGRlcml2YXRpdmUgb2JqZWN0IGNvbnRhaW5zIHRoZSBjYWxjdWxhdGVkIHNwZWVkIGFuZCBjaGFuZ2UgaW4gc3BlZWQgYWxvbmcgZWFjaCB0cmFqZWN0b3J5LiBUaGVzZSBhcmUgc3RvcmVkIGluIHRoZSBsaXN0IG9iamVjdDogZGVyaXZzX2xpc3QKCgpgYGB7ciBlY2hvPVRSVUV9CmRlcml2c19saXN0IDwtIHBtYXAobGlzdChjb29yZF9maWxlcywgc2NhbGVfZmlsZXMsIHRoZV9zY2FsZXMkc2NhbGVWYWx1ZXMpLCBsb2FkU2FtcGxlX0Zyb21MaXN0MikgJT4lIAogICMgc21vb3RoaW5nIHBhcmFtZXRlcnMgcGFzc2VkIHRvIFRyYWpTbW9vdFNHIGp1bmN0aW9uCiAgbWFwKFRyYWpTbW9vdGhTRywgcCA9IDUsIG4gPSAyMykgJT4lIAogIG1hcChUcmFqRGVyaXZhdGl2ZXMpICU+JSAKICAjIGVhY2ggZGVyaXZhdGl2ZXMgb2JqZWN0IGlzIGxhYmVsbGVkIHNlcXVlbnRpYWxseSB3aXRoIHRoZSBwcmVmaXg6IERlcml2cwogIHNldF9uYW1lcyhteWxhYmVsc2VxKCJEZXJpdnNfIiwgMSwgMjIsIDEpKQoKZGVyaXZzX25hbWVzIDwtIG5hbWVzKGRlcml2c19saXN0KQpgYGAKCkZyYW1lcyAxIHRvIDUgaW5kaWNhdGUgdGhlIHN0YXJ0IG9mIGVhY2ggc3RhZ2UgZHVyaW5nIHRoZSBzdHJpa2UgcGhhc2Ugb2YgdGhlIGFudCBzbGF5ZXIncyBhdHRhY2suClRoZSBmaXJzdCBtb3ZlIChhdHRhY2sgb25zZXQpIGlzIHJlY29yZGVkIGFzICJmcmFtZTEiLgpUbyBtYXRjaCB0aGUgZnJhbWUgbnVtYmVyIGF0IHdoaWNoIHRoZSBhdHRhY2sgb25zZXQgb2NjdXJzIHdpdGggaXRzIGluZGV4IHBvc2l0aW9uLCB0aGUgdmFsdWVzIGluICJwb3NGaWxlX3N0YXJ0IiBzaG91bGQgYmUgc3VidHJhY3RlZCBmcm9tIHRoZSBmcmFtZSBudW1iZXJzLgoKYGBge3IgZWNobz1UUlVFfQpmaXJzdF9tb3ZlIDwtIGFzLnZlY3Rvcih2aWRlb3NfcGFnZTEkZnJhbWUxIC0gdmlkZW9zX3BhZ2UxJHBvc0ZpbGVfc3RhcnQpCmBgYAoKVGhlIGZpcnN0IG1vdmUgZnJhbWUgaXMgdXNlZCB0byBjb250cm9sIGZvciBhbnkgc3BpZGVyIG1vdmVtZW50IGJlZm9yZSB0aGUgc3BpZGVyIHN0YXJ0ZWQgdGhlIGF0dGFjayBzZXF1ZW5jZS4KCmBgYHtyIGVjaG89VFJVRX0KZGVyaXZzX2xpc3RfMCA8LSBkZXJpdnNfbGlzdApmb3IoaSBpbiAxOmxlbmd0aChkZXJpdnNfbGlzdF8wKSkgewogIGRlcml2c19saXN0XzBbW2ldXSRzcGVlZFtjKDE6Zmlyc3RfbW92ZVtpXS0xKV0gPC0gMAp9CgpgYGAKCgoKYGBge3IgZWNobz1UUlVFfQoKIyBUcmltbWluZyBvZiB2aWRlbyB0aGUgZW5kIHBhcnQgb2YgdmlkZW8gMDMKIyBUaGUgbGFzdCBmcmFtZXMgaW4gdGhlIDNyZCB2aWRlbyBhcmUgdHJpbW1lZCBoZXJlIHRvIGNvcnJlY3QgZm9yIGEgbWlzdGFrZW5seSBzY29yZWQgcG9zaXRpb25zCgojIFRyaW1zIHRoZSBkZXJpdmF0aXZlcwpmb3IoaSBpbiAxOmxlbmd0aChkZXJpdnNfbGlzdF8wJERlcml2c18wMykpIHsKICBkZXJpdnNfbGlzdF8wJERlcml2c18wM1tbaV1dIDwtIAogICAgZGVyaXZzX2xpc3RfMCREZXJpdnNfMDNbW2ldXVtjKDE6MTIwKV0KfQoKIyBUcmltcyB0aGUgdHJhamVjdG9yeQphbGxfdHJhanMkVHJhal8wMyA8LSAKICBhbGxfdHJhanMkVHJhal8wM1sxOjEyMCwgXQoKYGBgCgoKPGJyPgoKCiMjIyBQZWFrIHNwZWVkIGZyYW1lIGluZGV4ZXMKCkhlcmUgd2UgY2FsY3VsYXRlZCB0aGUgcGVhayBzcGVlZCByZWFjaGVkIGJ5IHNwaWRlcnMgd2l0aGluIGEgZGVmaW5lZCB0aW1lIHdpbmRvdyB0aGF0IGNvdmVycyB0aGUgc3RyaWtlIHBoYXNlLgpUaGUgcmVzdWx0IGlzIHRoZSBpbmRleCBwb3NpdGlvbiB3aGVyZSB0aGUgcGVhayBzcGVlZCB3YXMgcmVhY2hlZC4KVGhlbiB0aGlzIGZyYW1lIGluZGV4IGNhbiBiZSB1c2VkIHRvIGFjY2VzcyB0aGUgdGltZSBhbmQgc3BlZWQgYXQgdGhhdCBtb21lbnQgKHNlZSBiZWxvdykuCgpgYGB7ciBlY2hvPVRSVUV9CiMgVGhlICJJbmRleExpbWl0c01hbnVhbCIgZnVuY3Rpb24gcHVsbHMgdGhlIGluZGV4IHBvc2l0aW9uIG9mIHRoZSBmb2xsb3dpbmcgZnJhbWUgaW5kZXggcG9zaXRpb246CiMgZnJhbWUgd2hlcmUgdGhlIGFjcm9iYXRpYyBzdHJpa2UgcmVhY2hlcyBpdHMgbWF4aW11bSBzcGVlZAoKIyBkZWZhdWx0IHZhbHVlcyB1c2VkIGZvciB0aGUgZnVuY3Rpb24KIyBJbmRleExpbWl0c01hbnVhbChkZXJpdnNfb2JqZWN0ID0gZGVyaXZzX2xpc3Rfc2FtcGxlJERlcml2c18wMSwgc3RhcnRUaHJlc2hvbGQgPSAxZS04LCB0aHJlc2hvbGRGcmFtZSA9IDEsIGxpbWl0X3JhbmdlID0gMTU1LCB3aW5kb3dfc2l6ZSA9IDM1KQoKaW5kZXhlc18wIDwtIG1hcChkZXJpdnNfbGlzdF8wLCBJbmRleExpbWl0c01hbnVhbCkgJT4lIG1hcCh+IHBsdWNrKC4sIDMpICU+JSBmaXJzdCgpKQojIGNoYW5nZSBuYW1lIHRvIHJlbW92ZSBzaG9ydAoKYGBgCgo8YnI+CgoKIyMgU3BlZWQgJiBUcmFqZWN0b3J5CgpUaGVzZSBwbG90cyBzaG93IHRoZSBzcGVlZCBjaGFuZ2UgZHVyaW5nIHRoZSBmaXJzdCBwaGFzZSBvZiB0aGUgYXR0YWNrIChhY3JvYmF0aWMgc3RyaWtlKSBhbmQgdGhlIHRyYWplY3RvcnkgZm9sbG93ZWQuCgpUaGUgYmx1ZSBhc3RlcmlzayBtYXJrcyB0aGUgb25zZXQgb2YgdGhlIHNwaWRlcnMnIGF0dGFjayAodGhlIHNwaWRlciBiZWdpbnMgbW92aW5nKS4KVGhlIHJlZCBhc3RlcmlzayBzaG93cyB3aGVyZSB0aGUgcGVhayBzcGVlZCB3YXMgcmVhY2hlZCBkdXJpbmcgdGhlIGFjcm9iYXRpYyBzdHJpa2UuCk5vdGUgdGhpcyBpcyByZXN0cmljdGVkIHRvIHRoZSBzcGVlZHMgYWNoaWV2ZWQgYnkgdGhlIHNwaWRlcnMgKip3aGlsZSoqIGRpcmVjdGluZyBpdHMgYXR0YWNrIHRvIHRoZSBhbnQgcHJleSBhbmQgKipiZWZvcmUqKiBkcm9wcGluZyBvZmYgZnJvbSB0aGUgdHJ1bmsgc3VyZmFjZS4KCmBgYHtyfQoKcGFyKG1mcm93ID0gYygyLDEpKQpmb3IoaSBpbiAxOmxlbmd0aChhbGxfdHJhanMpKSB7CiAgb25zZXRfdG9fcGVha19zcGVlZCA8LSAKICAgIGMoYyhmaXJzdF9tb3ZlW2ldLTEpLCBpbmRleGVzXzBbW2ldXSkKICAKICBwYXIobWZyb3cgPSBjKDEsMikpOyAKICBkZXJpdnNfbGlzdF8wW1tpXV0gJT4lIHBsb3RTcGVlZCgpOyAKICBwb2ludHMoCiAgICBkZXJpdnNfbGlzdF8wW1tpXV0kc3BlZWRbb25zZXRfdG9fcGVha19zcGVlZF0gfgogICAgICBkZXJpdnNfbGlzdF8wW1tpXV0kc3BlZWRUaW1lc1tvbnNldF90b19wZWFrX3NwZWVkXSwKICAgIHBjaCA9IDgsIGNvbCA9IGMoImJsdWUiLCAicmVkIikpCiAgCiAgcGxvdChhbGxfdHJhanNbW2ldXSwgbHdkID0gMSwgbHR5ID0gMSkKICBwb2ludHMoYWxsX3RyYWpzW1tpXV1bb25zZXRfdG9fcGVha19zcGVlZCwgXSwgcGNoID0gOCxjb2wgPSBjKCJibHVlIiwgInJlZCIpKQogIHRpdGxlKG1haW4gPSBuYW1lcyhhbGxfdHJhanNbaV0pLCBvdXRlciA9IFQsIGxpbmUgPSAtMikKfQoKYGBgCgo8YnI+CgojIyBTcGVlZCBjdXJ2ZXMgKyBlbGFwc2VkIHRpbWUgYm94cGxvdCB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30KCioqRmlndXJlIDEgUGFuZWwgRioqCgpUaGUgZmlndXJlIHBsb3RzIHRoZSBzcGVlZCBjaGFuZ2UgZnJvbSB0aGUgbGFzdCBmcmFtZSBiZWZvcmUgdGhlIGFjcm9iYXRpYyBhdHRhY2sgb25zZXQgdG8gdGhlIG1heGltdW0gc3BlZWQgcmVhY2hlZCBmb3IgdGhlIDIyIGluc3BlY3RlZCBoaWdoIHNwZWVkIHZpZGVvcy4KCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGxvb3AgbWUgdXAhCiMgY29sb3VyIHBhbGV0dGUKY2JwMSA8LSByZXYoYygiI0YwRTQ0MiIsICIjOTk5OTk5IiwgIiNFNjlGMDAiLCAiIzU2QjRFOSIsICIjMDA5RTczIiwKICAgICAgICAgICIjMDA3MkIyIiwgIiNENTVFMDAiLCAiI0NDNzlBNyIpKQojIHRvIHJlY3ljbGUgY29sb3VycwpjYnAxXzI0IDwtIHJlcChjYnAxLCAzKQoKbXAgPC0gZ2dwbG90KCkKZm9yKGkgaW4gc2VxX2Fsb25nKGRlcml2c19saXN0XzApKSB7CiAgCiAgb25zZXRfdG9fcGVha19zcGVlZCA8LSBjKGZpcnN0X21vdmVbaV0tMSk6aW5kZXhlc18wW1tpXV0KICAKICBpZGF0YSA8LSB0aWJibGUoInNwZWVkIiA9IGRlcml2c19saXN0XzBbW2ldXSRzcGVlZFtvbnNldF90b19wZWFrX3NwZWVkXSkgJT4lIAogICAgbXV0YXRlKCJpbmRleCIgPSBzZXEoMSxsZW5ndGgoc3BlZWQpLCAxKS0xKQogIAogIG1wIDwtIG1wICsgZ2VvbV9saW5lKGRhdGEgPSBpZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBpbmRleCwgeSA9IHNwZWVkKjEwMCksIGNvbG91ciA9IGNicDFfMjRbaV0pICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGlkYXRhLAogICAgICAgICAgICAgICBhZXMoeCA9IGxhc3QoaW5kZXgpLCAKICAgICAgICAgICAgICAgICAgIHkgPSAgbGFzdChzcGVlZCoxMDApKSwgCiAgICAgICAgICAgICAgIGNvbG91ciA9IGNicDFfMjRbaV0pICsKICAgIGV4cGFuZF9saW1pdHMoeCA9IGMoMCwgMzApLCB5ID0gYygwLCA2NSkpICsKICAgIHlsYWIoIlNwZWVkIChjbS9zKSIpICsKICAgIHhsYWIoIlRpbWUgKG1zKSIpICsKICAgIHRoZW1lX2NsYXNzaWMoKSArCiAgICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsKICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9ZnVuY3Rpb24oeCl4KjQsIG4uYnJlYWtzID0gNikKfQoKIyBwcmludChtcCkKYGBgCgoKCmBgYHtyfQojIE1heCBzcGVlZHMgZm9yIGVhY2ggc3BpZGVyIGFuZCB0aGUgZWxhcHNlZCB0aW1lIHdoaWxlIGdvaW5nIGZyb20gMCB0byB0byBtYXguCgp0YWJsZV9zcGVlZF9kdXJhdGlvbiA8LSAKICBtYXBfZGZyKGRlcml2c19saXN0XzAsIFNwZWVkQW5kVGltZTIpICU+JSAKICBtdXRhdGUoZnJvbSAgPSBkZXJpdnNfbmFtZXMsIC5iZWZvcmUgPSBtYXhfc3BlZWQpIAoKCmBgYAoKCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQojIFRoaXMgY29kZSBzaG93cyBob3cgdGhlIHBlYWsgc3BlZWRzIGFuZCBlbGFwc2VkIHRpbWUgd2VyZSBjYWxjdWxhdGVkIGluc2lkZSB0aGUgZnVuY3Rpb24gIlNwZWVkQW5kVGltZTIiCgptYXhfc3BlZWRzX3Nob3J0IDwtIHBtYXBfZGJsKGxpc3QoZGVyaXZzX2xpc3RfMCwgaW5kZXhlc18wKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfi4uMSRzcGVlZFsuLjJdKjEwMAopCgoKZHVyYXRpb25zIDwtIHBtYXBfZGJsKGxpc3QoZGVyaXZzX2xpc3RfMCwgaW5kZXhlc18wLCBmaXJzdF9tb3ZlKSwgCiAgICAgICAgICAgICAgICAgICAgICB+KC4uMSRzcGVlZFRpbWVzWy4uMl0gLSAKICAgICAgICAgICAgICAgICAgICAgICAgICAuLjEkc3BlZWRUaW1lc1tjKC4uM1sxXS0xKV0pKjEwMDAKKQoKYGBgCgoKCgpgYGB7cn0KCiMgSW5zZXQgYm94IHBsb3QgZm9yIGVsYXBzZWQgdGltZSBkdXJpbmcgc3BlZWQgY2hhbmdlIGluY2x1ZGVkIGluIGZpZ3VyZSAxIHBhbmVsIGYKCgojIGNyZWF0ZXMgYm94cGxvdCBmb3IgZWxhcHNlZCB0aW1lIGZyb20gMCB0byBwZWFrCmluc2V0MiA8LSB0YWJsZV9zcGVlZF9kdXJhdGlvbiAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9ib3hwbG90KGFlcyhlbGFwc2VkX3RpbWUpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKG4uYnJlYWtzID0gNCkgKwogIGV4cGFuZF9saW1pdHMoeCA9IGMoMCwgMTIwKSkKCiMgcmVtb3ZlcyBlbGVtZW50cyBmcm9tIHRoZSBib3hwbG90IHRvIG1ha2UgaXQgYW4gaW5zZXQgaW4gdGhlIHNwZWVkIGN1cnZlcyBwYW5lbAppbnNldDIgPC0gaW5zZXQyICsKdGhlbWUoIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLGNvbG91ciA9IE5BKSwKICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50Iixjb2xvdXIgPSBOQSksCiAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICMgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICApCmBgYAoKCiMjIyBQbG90CgpgYGB7cn0KZ2dwX2NvbWJpMiA8LSBpbnNldDIgLyBtcCArCiAgcGxvdF9sYXlvdXQoaGVpZ2h0cyA9IGMoLjI1LCA0KSkKZ2dwX2NvbWJpMgoKIyBnZ3NhdmUoZmlsZW5hbWUgPSAicGFuZWxGMy5wZGYiLCBwbG90ID0gZ2dwX2NvbWJpMiwgZGV2aWNlID0gInBkZiIsIHBhdGggPSAiLi9nZ19wbG90cyIpCmBgYAoKCiMjIyBEYXRhIHRhYmxlcwoKQ2FsY3VsYXRlZCBwZWFrIHNwZWVkcyBmb3IgZWFjaCBzcGlkZXIgYW5kIHRoZSBlbGFwc2VkIHRpbWUgd2hpbGUgZ29pbmcgZnJvbSAwIHRvIHRvIG1heAoKYGBge3J9CnRhYmxlX3NwZWVkX2R1cmF0aW9uICU+JSBrYWJsZUV4dHJhOjprYmwoKSAlPiUga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgaHRtbF9mb250ID0gIlRpbWVzIiwgYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCgpNZWFucyBhbmQgU0VNCgpgYGB7cn0KdGFibGVfc3BlZWRfZHVyYXRpb24gJT4lIAogIHN1bW1hcmlzZShhdmdfc3BlZWQgPSBtZWFuKG1heF9zcGVlZCksCiAgICAgICAgICAgIFNFTV9zcGVlZCA9IHN0ZC5lcnJvcihtYXhfc3BlZWQpLAogICAgICAgICAgICBhdmdfZHVyYXRpb24gPSBtZWFuKGVsYXBzZWRfdGltZSksCiAgICAgICAgICAgIFNFTV9kdXJhdGlvbiA9IHN0ZC5lcnJvcihlbGFwc2VkX3RpbWUpKSAlPiUga2FibGVFeHRyYTo6a2JsKCkgJT4lIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYsIGh0bWxfZm9udCA9ICJUaW1lcyIsIGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKCiMgcm91bmQobWVhbih0YWJsZV9zcGVlZF9kdXJhdGlvbiRtYXhfc3BlZWQpLCAyKQojIHJvdW5kKG1lYW4odGFibGVfc3BlZWRfZHVyYXRpb24kZWxhcHNlZF90aW1lKSwgMikKYGBgCgpNZWRpYW4gYW5kIGZyZXF1ZW5jaWVzIHRhYmxlIGZvciB0aGUgZWxhcHNlZCB0aW1lIChtcykKCmBgYHtyfQoKbWVkaWFuKHRhYmxlX3NwZWVkX2R1cmF0aW9uJGVsYXBzZWRfdGltZSkgCnRhYmxlKHRhYmxlX3NwZWVkX2R1cmF0aW9uJGVsYXBzZWRfdGltZSkKCmBgYAoKCiMjIFN1cHBsZW1lbnRhcnkKCldlIGV4cGxvcmVkIHRoZSBlZmZlY3Qgb2Ygc3BpZGVyIHNpemUgYW5kIG9yaWVudGF0aW9uIG9mIHRoZSBhdHRhY2sgKG1lYXN1cmVkIGFzIHRoZSBhbmdsZSB0b3dhcmRzIHdoaWNoIHRoZSBzcGlkZXIgZGlyZWN0ZWQgdGhlIHNvbWVyc2F1bHQgYXR0YWNrKS4KCiMjIyBTaXplIGFuZCBvcmllbnRhdGlvbgoKTW9kZWwgc3VtbWFyeQoKYGBge3J9CiMgY3JlYXRpbmcgZGF0YXNldCAKCnNpemVfc3BlZWRfb3JpZW50YXRpb24gPC0gYmluZF9jb2xzKAogIHRhYmxlX3NwZWVkX2R1cmF0aW9uLAogIHZpZGVvc19wYWdlMSAlPiUgCiAgICBzZWxlY3QoYHNwaWRlciBzaXplYCwgT3JpZW50YXRpb24pCikKCgojIHRoZSBtb2RlbCBHTE0KZ2xtX3NpemUgPC0gZ2xtKG1heF9zcGVlZCB+IGBzcGlkZXIgc2l6ZWAsIGZhbWlseSA9IEdhbW1hKGxpbmsgPSAibG9nIiksIAogICAgICAgICAgICAgICBkYXRhID0gc2l6ZV9zcGVlZF9vcmllbnRhdGlvbiAjICU+JSBmaWx0ZXIobWF4X3NwZWVkc19hbGwgPCA2MCkKKQojIE51bGwgbW9kZWwKZ2xtX251bGwgPC0gZ2xtKG1heF9zcGVlZCB+IDEsIGZhbWlseSA9IEdhbW1hKGxpbmsgPSAibG9nIiksIAogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBzaXplX3NwZWVkX29yaWVudGF0aW9uKQoKIyBzdW1tYXJ5CnN1bW1hcnkoZ2xtX3NpemUpCmBgYAoKRXhwbGFpbmVkIGRldmlhbmNlCgpgYGB7cn0KCmFub3ZhKGdsbV9zaXplLCBnbG1fbnVsbCwgdGVzdCA9ICJDaGkiKQojIG5vIHNpZ25pZmljYW50IGRpZmZlcmVuY2Ugd2l0aCB0aGUgbnVsbCBtb2RlbAoKbW9kX2RldiA8LSByb3VuZCgxMDAqKGdsbV9zaXplJG51bGwuZGV2aWFuY2UgLSBnbG1fc2l6ZSRkZXZpYW5jZSkvZ2xtX3NpemUkbnVsbC5kZXZpYW5jZSwgMikKCm1vZF9kZXYKIyAxNi43OCAlCgpgYGAKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMgc3VtbWFyeSB0YWJsZSB3aXRoIGFkZGl0aW9uYWwgbWV0aG9kIG5vdCB1c2VkIGZvciB0aGlzIHN1Ym1pc3Npb24KCnRhYl9tb2RlbChnbG1fc2l6ZSwgc2hvdy5zdGF0ID0gVCkKCiMgcGxvdChwcm9maWxlKGdsbV9zaXplKSkKCiMgYXNzZXNzbWVudCBvZiB0aGUgbW9kZWwgLW5vbiBwYXJhbWV0cmljIHRlc3Qgb2YgZml0CiMgbGlicmFyeShESEFSTWEpCiMgcmVzIDwtIHNpbXVsYXRlUmVzaWR1YWxzKGdsbV9zaXplKQojIHBsb3RRUXVuaWYocmVzKQojIHBsb3RSZXNpZHVhbHMocmVzKQoKYGBgCgoKYGBge3J9CnNwZWVkX3NpemVfcGxvdCA8LSBzaXplX3NwZWVkX29yaWVudGF0aW9uICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGBzcGlkZXIgc2l6ZWAsIHkgPSBtYXhfc3BlZWQpKSArCiAgc3RhdF9zbW9vdGgoYWVzKHggPSBgc3BpZGVyIHNpemVgLCB5ID0gbWF4X3NwZWVkKSwgCiAgICAgICAgICAgICAgbWV0aG9kID0gImdsbSIsIAogICAgICAgICAgICAgIGZvcm11bGEgPSB5fkkoMS94KSwKICAgICAgICAgICAgICBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5ID0gR2FtbWEobGluayA9ICJsb2ciKSksCiAgICAgICAgICAgICAgc2UgPSBUKSArCiAgbGFicyh4ID0gIlNwaWRlciBib2R5IGxlbmd0aCAobW0pIiwgeSA9ICJQZWFrIHNwZWVkIGNtL3MiKSArCiAgdGhlbWVfY2xhc3NpYygpCiMgc3BlZWRfc2l6ZV9wbG90CmBgYAoKCiMjIyBPcmllbnRhdGlvbgoKV2UgdGVzdGVkIHRoZSBhc3NvY2lhdGlvbiBiZXR3ZWVuIGxpbmVhciAobWF4aW11bSBzcGVlZCkgYW5kIGNpcmN1bGFyIChhdHRhY2sgb3JpZW50YXRpb24pIHZhcmlhYmxlcyB3aXRoIHRoZSBKb2huc29u4oCTV2Vocmx54oCTTWFyZGlhIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IChzZWUgY2hhcHRlciA4IGluIHJlZmVyZW5jZSBiZWxvdykuIFdoZXJlIFIy8J2RpfCdnIMgcmFuZ2VzIGJldHdlZW4gemVybyBhbmQgb25lLiBWYWx1ZXMgY2xvc2VyIHRvIG9uZSByZWZlciB0byBzdHJvbmdlciBhc3NvY2lhdGlvbnMuIFNwaWRlcnMgZGlkIG5vdCBleGhpYml0IGFueSBwcmVmZXJlbmNlIGZvciB0aGUgZGlyZWN0aW9uIGluIHdoaWNoIHRoZXkgYXR0YWNrZWQgdGhlIHByZXkKClBld3NleSBBLCBOZXVow6R1c2VyIE0sIFJ1eHRvbiBHRC4gQ2lyY3VsYXIgc3RhdGlzdGljcyBpbiBSLiBPeGZvcmQgVW5pdmVyc2l0eSBQcmVzczsgMjAxMy4KCgpgYGB7cn0KIyBjb252ZXJ0aW5nIGRlZ3JlZXMgdG8gcmFkaWFucwp3aXRoUmFkIDwtIHNpemVfc3BlZWRfb3JpZW50YXRpb24gJT4lIAogIG11dGF0ZShPcmlSYWQyID0gT3JpZW50YXRpb24qMipwaS8zNjApCgpSMnh0SW5kVGVzdFJhbmQod2l0aFJhZCRtYXhfc3BlZWQsIAogICAgICAgICAgICAgICAgd2l0aFJhZCRPcmlSYWQyLCA5OTk5KQoKYGBgCgpgYGB7cn0KZGlyZWN0aW9uX3Bsb3QgPC0gc2l6ZV9zcGVlZF9vcmllbnRhdGlvbiAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBhYnMoT3JpZW50YXRpb24pLCB5ID0gbWF4X3NwZWVkKSkgKwogIGxhYnMoeCA9ICJBdHRhY2sgZGlyZWN0aW9uIChkZWdyZWVzKSIsIHkgPSAiUGVhayBzcGVlZCBjbS9zIikgKwogIHRoZW1lX2NsYXNzaWMoKQojIGRpcmVjdGlvbl9wbG90CmBgYAoKIyMjIFN1cHBsZW1lbnRhcnkgcGxvdAoKYGBge3J9CnNwZWVkX3NpemVfcGxvdCArIGRpcmVjdGlvbl9wbG90ICsgcGxvdF9sYXlvdXQobmNvbCA9IDEpCmBgYAoKCgpgYGB7cn0KIyBzZXR3ZCgiL1VzZXJzL2FsZm9uc29hY2V2ZXMvRG93bmxvYWRzL0FudCBTbGF5ZXIgU3BlZWQvZ2dfcGxvdHMiKQojIHdyaXRlX2NzdihzcGVlZHNfdGltZXMsICJzcGVlZHNfdGltZXMuY3N2IikKYGBgCgoKPGJyPgoKOjo6IHsudG9jaWZ5LWV4dGVuZC1wYWdlIGRhdGEtdW5pcXVlPSJ0b2NpZnktZXh0ZW5kLXBhZ2UiIHN0eWxlPSJoZWlnaHQ6IDA7In0KOjo6Cg==