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:

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==