library(ggbeeswarm)
Loading required package: ggplot2
library(gt)
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
source(here::here("src/common_basis.R"))
here() starts at /Users/jiemakel/tyo/disc-analysis
── Attaching core tidyverse packages ──── tidyverse 2.0.0 ──
✔ dplyr     1.1.2     ✔ readr     2.1.4
✔ forcats   1.0.0     ✔ stringr   1.5.0
✔ lubridate 1.9.2     ✔ tibble    3.2.1
✔ purrr     1.0.1     ✔ tidyr     1.3.0── Conflicts ────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errorsRegistered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      

Post count distribution through time

active_posters <- incel_posts_c %>%
  count(year=year(time_posted),month=month(time_posted), poster_id) %>%
  filter(n>3) %>%
  count(year, month, name="value") %>%
  mutate(name="active posters") %>%
  collect()
incel_posts_c %>%
  group_by(year=year(time_posted),month=month(time_posted)) %>%
  summarise(posts=n(),users=n_distinct(poster_id), .groups="drop") %>%
  pivot_longer(posts:users) %>%
  collect() %>%
  union_all(active_posters) %>%
  mutate(month=as.Date(str_c(year,'-',month,'-01'))) %>%
  filter(month<"2023-03-01") %>%
  ggplot(aes(x=month,y=value)) +
  geom_line() +
  scale_y_continuous(labels=scales::number) + 
  scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
  xlab("Month") +
  ylab("N") +
  theme_hsci_discrete() +
  facet_wrap(~name,scales="free_y",ncol=1)

Post count distribution (overall/lounge)

quantiles <- seq(0,1,by=0.05)
incel_users_c %>%
  select(user_total_posts) %>%
  collect() %>%
  reframe(
    quantile=quantiles,
    user_total_posts=quantile(user_total_posts,quantiles)
  ) %>%
  inner_join(
    incel_posts_c %>%
      count(poster_id) %>%
      select(n) %>%
      collect() %>%
      reframe(
        quantile=quantiles,
        user_lounge_posts=quantile(n,quantiles)
    ),
    join_by(quantile)
  ) %>%
  gt(rowname_col = "quantile") %>%
  fmt_percent(quantile, drop_trailing_zeros = TRUE) %>%
  fmt_number(columns = c(user_total_posts,user_lounge_posts), drop_trailing_zeros = TRUE)
user_total_posts user_lounge_posts
0% 0 1
5% 2 1
10% 5 1
15% 8 1
20% 12 2
25% 18 3
30% 26 4
35% 36 5
40% 50 6
45% 69 8
50% 95.5 12
55% 126 16
60% 177 22
65% 247 31
70% 353 47
75% 536 74
80% 802.4 122
85% 1,337.65 228
90% 2,373.1 460
95% 5,339 1,223.65
100% 319,186 30,485

Interaction between join date and total / lounge posts

incel_users_c %>%
  filter(user_joined>"1970-01-01") %>%
  ggplot(aes(x=user_joined,y=user_total_posts)) +
  geom_point(size=0.5) +
  geom_smooth(method="lm", formula="y~x") +
  theme_hsci_discrete() +
  scale_y_continuous(labels=scales::number) +
  xlab("user join date") +
  ylab("Total posts") +
  ggtitle("Total posts")

incel_posts_c %>%
  count(poster_id) %>%
  inner_join(incel_users_c, join_by(poster_id==user_id)) %>%
  filter(user_joined>"1970-01-01") %>%
  ggplot(aes(x=user_joined,y=n)) +
  geom_point(size=0.5) +
  geom_smooth(method="lm", formula="y~x") +
  theme_hsci_discrete() +
  scale_y_continuous(labels=scales::number) +
  xlab("user join date") +
  ylab("Lounge posts") +
  ggtitle("Lounge posts")

Board rhythm

incel_posts_c %>% 
  mutate(hour=hour(time_posted),weekday=weekday(time_posted)) %>%
  count(weekday,hour) %>%
  ggplot(aes(x=hour,y=n,color=as_factor(weekday))) +
  geom_line() +
  theme_hsci_discrete() +
  theme(
    legend.justification = c(1, 0), 
    legend.position = c(0.98, 0.02), 
    legend.background = element_blank(), 
    legend.key = element_blank()) +
  xlab("Hour (UTC)") +
  ylab("Total number of posts") +
  labs(color="Day of the week") +
  ggtitle("Posts by the time of day")

weekdays <- tribble(~index,~weekday,
                    0,"Mon",
                    1,"Tue",
                    2,"Wed",
                    3,"Thu",
                    4,"Fri",
                    5,"Sat",
                    6,"Sun")
incel_posts_c %>% 
  mutate(weekday=weekday(time_posted)) %>%
  count(weekday) %>%
  ggplot(aes(x=weekday,y=n)) +
  geom_col() +
  theme_hsci_discrete() +
  scale_x_continuous(breaks=weekdays$index, labels=weekdays$weekday) +
  scale_y_continuous(labels=scales::number) +
  xlab("Day of the week") +
  ylab("Total number of posts") +
  ggtitle("Posts by day of the week")

user_type <- incel_posts_c %>%
  count(poster_id) %>%
  mutate(user_type=if_else(n>=100,"top","other")) %>%
  select(poster_id, user_type)

incel_posts_c %>% 
  mutate(hour=hour(time_posted)) %>%
  inner_join(user_type, join_by(poster_id)) %>%
  count(user_type,hour) %>%
  group_by(user_type) %>%
  mutate(proportion=n/sum(n)) %>%
  ungroup() %>%
  ggplot(aes(x=hour,y=proportion,color=user_type)) +
  geom_line() +
  theme_hsci_discrete() +
  theme(
    legend.justification = c(1, 0), 
    legend.position = c(0.98, 0.02), 
    legend.background = element_blank(), 
    legend.box.just = "bottom", 
    legend.key = element_blank(), 
    legend.box = "horizontal") +
  scale_y_continuous(labels=scales::percent_format(accuracy=1)) +
  xlab("Hour (UTC)") +
  ylab("Proportion of posts") +
  labs(color="user type") +
  ggtitle("Proportion of posts by the time of day of top/other users")
Warning: Missing values are always removed in SQL aggregation
functions.
Use `na.rm = TRUE` to silence this warning

Are there distinct subpopulations?

incel_posts_c %>% 
  mutate(hour=hour(time_posted)) %>%
  count(poster_id,hour) %>%
  group_by(poster_id) %>%
  filter(sum(n)>=100) %>% # limit to users with enough data to get any pattern
  mutate(proportion=n/sum(n)) %>%
  ungroup() %>%
  ggplot(aes(x=hour,y=proportion)) +
  geom_quasirandom(size=0.25) +
  coord_cartesian(ylim=c(0,0.25)) +
  theme_hsci_discrete() +
  scale_y_continuous(labels=scales::percent_format(accuracy=1)) +
  xlab("Hour (UTC)") +
  ylab("Proportion of posts") +
  labs(color="user type") +
  ggtitle("Proportion of posts by the time of day for each individual user")

  • There do not seem to be clearly distinct time profiles with large groups of users. There may be some variation in UTC night time posting behaviour (3-12 UTC)

How long are people active by year joined

incel_posts_c %>%
    group_by(poster_id) %>%
    summarise(earliest_post=min(time_posted),latest_post=max(time_posted), .groups="drop") %>%
    mutate(earliest_post_year=year(earliest_post), active_period_days=sql("timestampdiff(day,earliest_post,latest_post)")) %>%
  ggplot(aes(x=earliest_post_year,y=active_period_days)) + 
  geom_quasirandom(size=0.25) +
  theme_hsci_discrete() +
  xlab("Year of earliest post") +
  ylab("Time between earliest and latest post (days)") +
  ggtitle("Time between earliest and latest post by year joined")

Different -cels

cel_post_contents <- incel_posts_c %>% 
  filter(str_detect(post_content,"cels?\\b")) %>% 
  select(post_id, post_content) %>% 
  collect() 

cels_by_post <- cel_post_contents %>%
  mutate(cel=str_extract_all(post_content, "(?<! expand...)[^ \\n]+ cels?\\b|[^ \\n]*cels?\\b(?! said)")) %>%
  unnest(cel) %>% 
  filter(!str_detect(cel, "^@")) %>%
  select(post_id, cel) %>% 
  mutate(cel=cel %>% 
           str_to_lower() %>% 
           str_replace_all("\\W","") %>% 
           str_replace_all("s$",""))
cels <- cels_by_post %>%
  count(cel) %>% 
  arrange(desc(n))
cels %>% 
  write_tsv(here("data/output/jiemakel/cels.tsv"),na="",quote="needed")
cels %>%
  head(n=100) %>%
  gt(rowname_col="cel") %>%
  fmt_integer(n)
n
incel 116,353
fakecel 14,234
truecel 10,269
volcel 7,820
greycel 7,145
brocel 6,364
httpsincel 5,779
graycel 5,446
trucel 4,641
ricecel 4,330
currycel 3,160
mentalcel 2,309
oldcel 2,243
gymcel 2,185
youngcel 2,030
blackcel 1,977
lostcel 1,977
braincel 1,628
femcel 1,626
rincel 1,588
ritalincel 1,397
escortcel 1,329
whitecel 1,267
fatcel 1,261
excel 994
maycel 983
poorcel 921
blackops2cel 830
newcel 823
fbicel 788
weebcel 758
iqcel 745
richcel 723
cancel 661
framecel 604
bluecel 570
fellowcel 561
sandcel 521
gaycel 508
ethnicel 488
stormfrontcel 459
animecel 443
2019cel 439
2017cel 436
itcel 420
slavcel 399
tallcel 397
shortcel 373
rbraincel 367
chadcel 366
stemcel 345
2020cel 336
neetcel 316
diocel 313
pedocel 313
teencel 313
ukcel 313
2018cel 304
cel 299
kikecel 297
ethniccel 282
baldcel 277
muslimcel 271
ogrecel 267
standardcel 267
junecel 262
burgercel 259
wristcel 252
ksgcel 251
modcel 241
locationcel 233
mayocel 231
lowiqcel 229
ppecel 229
sergeantincel 228
sfcel 223
2022cel 220
cuckcel 213
voicecel 208
nonincel 205
ogcel 202
jewcel 201
dickcel 200
studycel 198
novembercel 197
antiincel 196
nazicel 194
eskimocel 185
finncel 183
rightfulcel 182
stormcel 177
augustcel 174
octobercel 174
usacel 174
femdomcel 173
bagelcel 170
nearcel 168
arabcel 161
autistcel 160
chatcel 159
cels2 <- cels_by_post %>%
  filter(cel!="incel") %>%
  distinct() %>%
  group_by(post_id) %>%
  filter(n()>1) %>%
  arrange(cel) %>%
  summarise(cel=str_flatten(cel, collapse=", "), .groups="drop") %>%
  count(cel) %>% 
  arrange(desc(n))
cels2 %>% 
  write_tsv(here("data/output/jiemakel/cels2.tsv"),na="",quote="needed")
cels2 %>%
  head(n=100) %>%
gt(rowname_col="cel") %>%
  fmt_integer(n)
n
fakecel, truecel 504
fatcel, volcel 246
fakecel, trucel 148
graycel, greycel 143
fakecel, volcel 126
trucel, truecel 121
fakecel, mentalcel 106
currycel, ricecel 103
bluecel, greycel 94
currycel, trucel 90
fakecel, youngcel 90
oldcel, youngcel 81
mentalcel, truecel 76
fakecel, greycel 66
femcel, volcel 65
mentalcel, volcel 62
truecel, volcel 61
fakecel, graycel 58
fakecel, whitecel 55
rbraincel, rincel 49
aaaaaaaaaaacel, cheesecel, daydreamincel, diocel, itsover4cel, manicel, rightfulcel, singleplayercel 47
lostcel, trucel 46
gaycel, maycel 43
brocel, fakecel 42
escortcel, fakecel 42
junecel, maycel 41
braincel, rincel 40
fakecel, httpsincel 40
standardcel, volcel 38
gaycel, greycel 35
fakecel, fatcel 32
bluecel, graycel 31
greycel, truecel 31
brocel, truecel 30
oldcel, truecel 30
brocel, trucel 29
fakecel, ricecel 28
currycel, truecel 27
brocel, graycel 26
ethnicel, whitecel 26
chadcel, mentalcel 25
fakecel, rincel 25
fakecel, tallcel 25
httpsincel, truecel 25
blackcel, ricecel 24
ricecel, truecel 24
volcel, whitecel 24
blackcel, whitecel 23
fakecel, oldcel 23
poorcel, richcel 22
braincel, rbraincel 21
fakecel, richcel 21
ethniccel, whitecel 20
rincel, rtruecel 20
truecel, youngcel 20
2019cel, 2020cel 19
blackcel, fakecel 18
currycel, fakecel 18
escortcel, volcel 18
fakecel, maycel 18
fakecel, newcel 18
ricecel, whitecel 18
aaaaaaaaaaacel, cheesecel, itsover4cel, singleplayercel 17
brocel, greycel 17
fatcel, truecel 17
graycel, truecel 17
greycel, httpsincel 17
greycel, newcel 17
ricecel, trucel 17
rincel, truecel 17
2018cel, 2019cel 16
braincel, fakecel 16
brocel, httpsincel 16
fakecel, femcel 16
fakecel, rightfulcel 16
fakecel, truestcel 16
graycel, gymcel 16
greycel, trucel 16
2022cel, 2023cel 15
alcoholiccel, homelesscel 15
currycel, volcel 15
escortcel, fakecel, httpsincel, mentacel 15
fakecel, locationcel 15
gaycel, graycel 15
gaycel, volcel 15
mentalcel, trucel 15
tallcel, volcel 15
trucel, volcel 15
truecel, whitecel 15
braincel, femcel 14
framecel, gymcel 14
graycel, trucel 14
graycel, volcel 14
httpsincel, volcel 14
oldcel, trucel 14
2017cel, fakecel 13
blackcel, volcel 13
escortcel, fakecel, mentacel 13
fakecel, mentalcel, truecel 13
graycel, httpsincel 13
trucel_posts <- cels_by_post %>% 
  filter(str_detect(cel,"tru")) %>%
  distinct(post_id)

fakecel_posts <- cels_by_post %>% 
  filter(str_detect(cel,"fake")) %>%
  distinct(post_id)
cels_by_post %>% inner_join(trucel_posts, join_by(post_id)) %>%
  filter(!cel %in% c("trucel","truecel", "incel", "httpsincel")) %>%
  count(cel) %>%
  arrange(desc(n))

cels_by_post %>% inner_join(fakecel_posts, join_by(post_id)) %>%
  filter(!cel %in% c("fakecel","incel", "httpsincel")) %>%
  count(cel) %>%
  arrange(desc(n))

You/we/they are

are_post_contents <- incel_posts_c %>% 
  filter(str_detect(post_content,"(you|they|we)('re| are) ")) %>% 
  select(post_id, post_content) %>% 
  collect() %>%
  mutate(post_content = post_content %>% str_replace_all("Click to expand...",".") %>% str_replace_all("\\s+"," "))
ares_by_post <- c(1:4) %>% 
  map_dfr(~are_post_contents %>%
    mutate(length= .x, are=str_extract_all(post_content, str_c("(you|they|we)('re| are)('nt| not)?( a| an| the)?", strrep(" \\w+",.x))))
  ) %>%  
  unnest(are) %>%
  select(post_id, are, length) %>%
  mutate(are=are %>% str_replace("'re"," are") %>% str_replace("'nt", " not")) %>%
  mutate(
    who=str_replace(are," .*",""), 
    are=str_replace(are, ".*? ",""),
    stem=str_replace(are, " [^ ]*$", "")
  ) %>%
  relocate(post_id, length, who, are)
ares_by_post
ares_by_post_count <- ares_by_post %>%
  count(length, who, are, stem)
ares_by_post_count
top_ares <- ares_by_post_count %>% 
  group_by(length, who) %>%
  slice_max(n,n=20) %>%
  ungroup()
ares_by_post_count %>% 
  anti_join(top_ares, join_by(who, are==stem)) %>%
  anti_join(ares_by_post_count %>% mutate(length=length+1), join_by(who,are,length)) %>%
  select(-stem) %>%
  group_by(length, who) %>%
  slice_max(n,n=20) %>%
  mutate(order=row_number()) %>%
  ungroup() %>%
  filter(order<=20) %>%
  pivot_wider(id_cols=c("length","order"), names_from="who", values_from=c("are","n")) %>%
  relocate(are_they,n_they,are_we,n_we,are_you,n_you) %>%
  arrange(desc(length)) %>%
  gt(groupname_col = "length", rowname_col="order") %>%
  cols_label(
    are_they="They",
    n_they="N",
    are_we="We",
    n_we="N",
    are_you="You",
    n_you="N") %>%
  tab_style(
    style = list(
      cell_borders(
        sides = c("right"),
        style = "solid"
      )
    ),
    locations = cells_body(
      columns = c(n_we,n_you,n_they)
    )
  )
They N We N You N
4
1 are mistaken and should relieve 51 are neurologically constrained from simultaneously 30 are for replying on a 105
2 are just a bunch of 40 are all in this together 26 are a disgrace to your family 80
3 are all a bunch of 25 are actively trying to improve 23 are going to have to 74
4 are being raped all over 22 are all in the same 22 are going to have a 30
5 are victimized every single time 22 are just a bunch of 19 are pretending to be incels 29
6 are weird and sad lunatics 17 are the ones that need to 18 are nothing but LARPers who 28
7 are more likely to be 16 are weird creepy cult they 18 are too low IQ to 26
8 are just pretending to love 15 are on the same page 16 are too much of a 26
9 are not thinking of the father 15 are in the same boat 15 are LITERALLY dedicating at least 24
10 are the scum of the earth 15 are all gonna make it 14 are a man of culture as 24
11 are all the same thing 14 are concerned specifically with the 14 are incel of whether you 24
12 are going to have to 14 are all going to be 11 are going to be a 23
13 are an agreed upon social harm 12 are back to the bragging 11 are one of my favorite 21
14 are drawn to the edgier 12 are supposed to be the 11 are one of the few 21
15 are in the same boat 12 are all a bunch of 10 are a nincompoop will still be 20
16 are one of the most 12 are all going to die 10 are trying to tell me 19
17 are some of the most 12 are going to talk about 10 are never going to get 18
18 are extremely fragile and insecure 11 are talking about the same 10 are one of the most 18
19 are much more likely to 11 are at the bottom of 9 are just throwing posts into 17
20 are very easy on the 11 are going to have a 9 are rating is so far 17
3
1 are going to be 81 are going to be 35 are going to get 116
2 are supposed to be 61 are all the same 29 are one of those 86
3 are part of the 53 are living in the 25 are trying to say 80
4 are less likely to 35 are going to get 20 are paying the price 78
5 are trying to make 35 are radical and misogynistic 18 are just going to 63
6 are going to do 32 are here to suffer 17 are going to do 57
7 are just trying to 29 are missing out on 17 are missing out on 55
8 are better than you 28 are gonna have to 14 are part of the 54
9 are the only ones who 28 are talking about here 14 are good to go 53
10 are going to get 27 are the only ones who 13 are more likely to 50
11 are too stupid to 27 are going to do 12 are on this forum 49
12 are better than us 26 are part of the 12 are one of them 46
13 are nothing more than 26 are going to say 11 are supposed to be 46
14 are genuinely fucking retarded 25 are not entitled to sex 11 are no better than 45
15 are trying to get 24 are talking about a 11 are talking about the 45
16 are going to make 23 are in a simulation 10 are gonna have to 44
17 are full of shit 22 are in this together 10 are still an incel 44
18 are some kind of 22 are led to believe 10 are still a virgin 39
19 are just going to 21 are never going to 10 are nothing but a 36
20 are made out of 21 are stuck in a 10 are in the US 35
2
1 are talking about 184 are in a 87 are talking about 1013
2 are in the 160 are forced to 57 are just a 386
3 are able to 121 are at the 50 are in the 376
4 are attracted to 121 are here to 47 are in a 351
5 are willing to 119 are able to 45 are looking for 311
6 are forced to 111 are trying to 45 are able to 245
7 are just as 100 are dealing with 38 are interested in 225
8 are in a 95 are gonna have 38 are still a 224
9 are doing it 90 are at it 37 are willing to 196
10 are entitled to 84 are all here 36 are not entitled to 176
11 are full of 81 are in this 34 are not going to 157
12 are on the 81 are all gonna 33 are such a 157
13 are in their 80 are used to 33 are forced to 153
14 are a bunch of 76 are part of 32 are doing it 149
15 are no longer 76 are gonna be 31 are referring to 147
16 are not going to 72 are stuck in 31 are good at 144
17 are allowed to 71 are the bad guys 31 are good looking 144
18 are the ones who 71 are all just 30 are on the 141
19 are afraid of 67 are on a 30 are reading this 140
20 are too busy 66 are not going to 29 are ugly and 134
1
1 are so 1046 are just 404 are right 1471
2 are still 879 are still 217 are doing 1208
3 are too 735 are so 215 are ugly 1093
4 are more 667 are both 179 are so 1004
5 are also 427 are ugly 163 are saying 914
6 are good 408 are getting 149 are gonna 878
7 are very 402 are too 136 are incel 850
8 are both 382 are being 135 are on 831
9 are being 374 are incels 128 are an incel 723
10 are gonna 356 are already 120 are too 660
11 are really 348 are doing 112 are good 594
12 are probably 346 are now 89 are white 573
13 are like 341 are to 88 are probably 559
14 are fucking 338 are fucked 85 are at 541
15 are pretty 332 are more 85 are being 496
16 are getting 328 are like 83 are getting 489
17 are actually 317 are stuck 81 are already 477
18 are always 316 are not 76 are really 470
19 are not 312 are the ones 76 are actually 404
20 are ugly 309 are born 75 are here 400
NA
LS0tCnRpdGxlOiAiSW5jZWwgYW5hbHlzaXMiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogIG1kX2RvY3VtZW50OgogICAgdmFyaWFudDogZ2ZtIAogICAgdG9jOiB5ZXMKLS0tCgpgYGB7ciBzZXR1cH0KbGlicmFyeShnZ2JlZXN3YXJtKQpsaWJyYXJ5KGd0KQpzb3VyY2UoaGVyZTo6aGVyZSgic3JjL2NvbW1vbl9iYXNpcy5SIikpCmBgYAoKIyBQb3N0IGNvdW50IGRpc3RyaWJ1dGlvbiB0aHJvdWdoIHRpbWUKCmBgYHtyfQphY3RpdmVfcG9zdGVycyA8LSBpbmNlbF9wb3N0c19jICU+JQogIGNvdW50KHllYXI9eWVhcih0aW1lX3Bvc3RlZCksbW9udGg9bW9udGgodGltZV9wb3N0ZWQpLCBwb3N0ZXJfaWQpICU+JQogIGZpbHRlcihuPjMpICU+JQogIGNvdW50KHllYXIsIG1vbnRoLCBuYW1lPSJ2YWx1ZSIpICU+JQogIG11dGF0ZShuYW1lPSJhY3RpdmUgcG9zdGVycyIpICU+JQogIGNvbGxlY3QoKQpgYGAKCmBgYHtyfQppbmNlbF9wb3N0c19jICU+JQogIGdyb3VwX2J5KHllYXI9eWVhcih0aW1lX3Bvc3RlZCksbW9udGg9bW9udGgodGltZV9wb3N0ZWQpKSAlPiUKICBzdW1tYXJpc2UocG9zdHM9bigpLHVzZXJzPW5fZGlzdGluY3QocG9zdGVyX2lkKSwgLmdyb3Vwcz0iZHJvcCIpICU+JQogIHBpdm90X2xvbmdlcihwb3N0czp1c2VycykgJT4lCiAgY29sbGVjdCgpICU+JQogIHVuaW9uX2FsbChhY3RpdmVfcG9zdGVycykgJT4lCiAgbXV0YXRlKG1vbnRoPWFzLkRhdGUoc3RyX2MoeWVhciwnLScsbW9udGgsJy0wMScpKSkgJT4lCiAgZmlsdGVyKG1vbnRoPCIyMDIzLTAzLTAxIikgJT4lCiAgZ2dwbG90KGFlcyh4PW1vbnRoLHk9dmFsdWUpKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpudW1iZXIpICsgCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgeWVhciIsIGRhdGVfbGFiZWxzID0gIiVZIikgKwogIHhsYWIoIk1vbnRoIikgKwogIHlsYWIoIk4iKSArCiAgdGhlbWVfaHNjaV9kaXNjcmV0ZSgpICsKICBmYWNldF93cmFwKH5uYW1lLHNjYWxlcz0iZnJlZV95IixuY29sPTEpCmBgYAoKIyBQb3N0IGNvdW50IGRpc3RyaWJ1dGlvbiAob3ZlcmFsbC9sb3VuZ2UpCgpgYGB7cn0KcXVhbnRpbGVzIDwtIHNlcSgwLDEsYnk9MC4wNSkKaW5jZWxfdXNlcnNfYyAlPiUKICBzZWxlY3QodXNlcl90b3RhbF9wb3N0cykgJT4lCiAgY29sbGVjdCgpICU+JQogIHJlZnJhbWUoCiAgICBxdWFudGlsZT1xdWFudGlsZXMsCiAgICB1c2VyX3RvdGFsX3Bvc3RzPXF1YW50aWxlKHVzZXJfdG90YWxfcG9zdHMscXVhbnRpbGVzKQogICkgJT4lCiAgaW5uZXJfam9pbigKICAgIGluY2VsX3Bvc3RzX2MgJT4lCiAgICAgIGNvdW50KHBvc3Rlcl9pZCkgJT4lCiAgICAgIHNlbGVjdChuKSAlPiUKICAgICAgY29sbGVjdCgpICU+JQogICAgICByZWZyYW1lKAogICAgICAgIHF1YW50aWxlPXF1YW50aWxlcywKICAgICAgICB1c2VyX2xvdW5nZV9wb3N0cz1xdWFudGlsZShuLHF1YW50aWxlcykKICAgICksCiAgICBqb2luX2J5KHF1YW50aWxlKQogICkgJT4lCiAgZ3Qocm93bmFtZV9jb2wgPSAicXVhbnRpbGUiKSAlPiUKICBmbXRfcGVyY2VudChxdWFudGlsZSwgZHJvcF90cmFpbGluZ196ZXJvcyA9IFRSVUUpICU+JQogIGZtdF9udW1iZXIoY29sdW1ucyA9IGModXNlcl90b3RhbF9wb3N0cyx1c2VyX2xvdW5nZV9wb3N0cyksIGRyb3BfdHJhaWxpbmdfemVyb3MgPSBUUlVFKQpgYGAKCiAqIEFzIGV4cGVjdGVkLCB0aGUgZGlzdHJpYnV0aW9uIGlzIHZlcnkgc2tld2VkLiBIYWxmIHRoZSB1c2VycyBoYXZlIGxlc3MgdGhhbiAxMDAgcG9zdHMsIHdoaWxlIHRoZSB0b3AgMjUlIGhhdmUgbW9yZSB0aGFuIDUwMC4gCgojIEludGVyYWN0aW9uIGJldHdlZW4gam9pbiBkYXRlIGFuZCB0b3RhbCAvIGxvdW5nZSBwb3N0cwoKYGBge3J9CmluY2VsX3VzZXJzX2MgJT4lCiAgZmlsdGVyKHVzZXJfam9pbmVkPiIxOTcwLTAxLTAxIikgJT4lCiAgZ2dwbG90KGFlcyh4PXVzZXJfam9pbmVkLHk9dXNlcl90b3RhbF9wb3N0cykpICsKICBnZW9tX3BvaW50KHNpemU9MC41KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGZvcm11bGE9Inl+eCIpICsKICB0aGVtZV9oc2NpX2Rpc2NyZXRlKCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpudW1iZXIpICsKICB4bGFiKCJ1c2VyIGpvaW4gZGF0ZSIpICsKICB5bGFiKCJUb3RhbCBwb3N0cyIpICsKICBnZ3RpdGxlKCJUb3RhbCBwb3N0cyIpCmBgYAoKYGBge3J9CmluY2VsX3Bvc3RzX2MgJT4lCiAgY291bnQocG9zdGVyX2lkKSAlPiUKICBpbm5lcl9qb2luKGluY2VsX3VzZXJzX2MsIGpvaW5fYnkocG9zdGVyX2lkPT11c2VyX2lkKSkgJT4lCiAgZmlsdGVyKHVzZXJfam9pbmVkPiIxOTcwLTAxLTAxIikgJT4lCiAgZ2dwbG90KGFlcyh4PXVzZXJfam9pbmVkLHk9bikpICsKICBnZW9tX3BvaW50KHNpemU9MC41KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGZvcm11bGE9Inl+eCIpICsKICB0aGVtZV9oc2NpX2Rpc2NyZXRlKCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpudW1iZXIpICsKICB4bGFiKCJ1c2VyIGpvaW4gZGF0ZSIpICsKICB5bGFiKCJMb3VuZ2UgcG9zdHMiKSArCiAgZ2d0aXRsZSgiTG91bmdlIHBvc3RzIikKYGBgCgogKiBUaGUgZWFybGllciB5b3Ugam9pbiwgdGhlIG1vcmUgbGlrZWx5IHlvdSBhcmUgdG8gaGF2ZSBtb3JlIHBvc3RzLCBidXQgdGhlcmUgZG9lc24ndCBzZWVtIHRvIGJlIGEgZGlzY2VybmlibGUgcGF0dGVybiBmb3Igd2hlbiB0aGUgcmVhbCAiaGVhdnkgaGl0dGVycyIgaGF2ZSBqb2luZWQuCgojIEJvYXJkIHJoeXRobQoKYGBge3J9CmluY2VsX3Bvc3RzX2MgJT4lIAogIG11dGF0ZShob3VyPWhvdXIodGltZV9wb3N0ZWQpLHdlZWtkYXk9d2Vla2RheSh0aW1lX3Bvc3RlZCkpICU+JQogIGNvdW50KHdlZWtkYXksaG91cikgJT4lCiAgZ2dwbG90KGFlcyh4PWhvdXIseT1uLGNvbG9yPWFzX2ZhY3Rvcih3ZWVrZGF5KSkpICsKICBnZW9tX2xpbmUoKSArCiAgdGhlbWVfaHNjaV9kaXNjcmV0ZSgpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygxLCAwKSwgCiAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuOTgsIDAuMDIpLCAKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCkpICsKICB4bGFiKCJIb3VyIChVVEMpIikgKwogIHlsYWIoIlRvdGFsIG51bWJlciBvZiBwb3N0cyIpICsKICBsYWJzKGNvbG9yPSJEYXkgb2YgdGhlIHdlZWsiKSArCiAgZ2d0aXRsZSgiUG9zdHMgYnkgdGhlIHRpbWUgb2YgZGF5IikKYGBgCgpgYGB7cn0Kd2Vla2RheXMgPC0gdHJpYmJsZSh+aW5kZXgsfndlZWtkYXksCiAgICAgICAgICAgICAgICAgICAgMCwiTW9uIiwKICAgICAgICAgICAgICAgICAgICAxLCJUdWUiLAogICAgICAgICAgICAgICAgICAgIDIsIldlZCIsCiAgICAgICAgICAgICAgICAgICAgMywiVGh1IiwKICAgICAgICAgICAgICAgICAgICA0LCJGcmkiLAogICAgICAgICAgICAgICAgICAgIDUsIlNhdCIsCiAgICAgICAgICAgICAgICAgICAgNiwiU3VuIikKaW5jZWxfcG9zdHNfYyAlPiUgCiAgbXV0YXRlKHdlZWtkYXk9d2Vla2RheSh0aW1lX3Bvc3RlZCkpICU+JQogIGNvdW50KHdlZWtkYXkpICU+JQogIGdncGxvdChhZXMoeD13ZWVrZGF5LHk9bikpICsKICBnZW9tX2NvbCgpICsKICB0aGVtZV9oc2NpX2Rpc2NyZXRlKCkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9d2Vla2RheXMkaW5kZXgsIGxhYmVscz13ZWVrZGF5cyR3ZWVrZGF5KSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6Om51bWJlcikgKwogIHhsYWIoIkRheSBvZiB0aGUgd2VlayIpICsKICB5bGFiKCJUb3RhbCBudW1iZXIgb2YgcG9zdHMiKSArCiAgZ2d0aXRsZSgiUG9zdHMgYnkgZGF5IG9mIHRoZSB3ZWVrIikKYGBgCgoKYGBge3J9CnVzZXJfdHlwZSA8LSBpbmNlbF9wb3N0c19jICU+JQogIGNvdW50KHBvc3Rlcl9pZCkgJT4lCiAgbXV0YXRlKHVzZXJfdHlwZT1pZl9lbHNlKG4+PTEwMCwidG9wIiwib3RoZXIiKSkgJT4lCiAgc2VsZWN0KHBvc3Rlcl9pZCwgdXNlcl90eXBlKQoKaW5jZWxfcG9zdHNfYyAlPiUgCiAgbXV0YXRlKGhvdXI9aG91cih0aW1lX3Bvc3RlZCkpICU+JQogIGlubmVyX2pvaW4odXNlcl90eXBlLCBqb2luX2J5KHBvc3Rlcl9pZCkpICU+JQogIGNvdW50KHVzZXJfdHlwZSxob3VyKSAlPiUKICBncm91cF9ieSh1c2VyX3R5cGUpICU+JQogIG11dGF0ZShwcm9wb3J0aW9uPW4vc3VtKG4pKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ2dwbG90KGFlcyh4PWhvdXIseT1wcm9wb3J0aW9uLGNvbG9yPXVzZXJfdHlwZSkpICsKICBnZW9tX2xpbmUoKSArCiAgdGhlbWVfaHNjaV9kaXNjcmV0ZSgpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygxLCAwKSwgCiAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuOTgsIDAuMDIpLCAKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgIGxlZ2VuZC5ib3guanVzdCA9ICJib3R0b20iLCAKICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCksIAogICAgbGVnZW5kLmJveCA9ICJob3Jpem9udGFsIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50X2Zvcm1hdChhY2N1cmFjeT0xKSkgKwogIHhsYWIoIkhvdXIgKFVUQykiKSArCiAgeWxhYigiUHJvcG9ydGlvbiBvZiBwb3N0cyIpICsKICBsYWJzKGNvbG9yPSJ1c2VyIHR5cGUiKSArCiAgZ2d0aXRsZSgiUHJvcG9ydGlvbiBvZiBwb3N0cyBieSB0aGUgdGltZSBvZiBkYXkgb2YgdG9wL290aGVyIHVzZXJzIikKYGBgCgogKiBUaGVyZSBkb2Vzbid0IHNlZW0gdG8gYmUgYSBkaWZmZXJlbmNlIGluIGRhaWx5IHJoeXRobXMgYmV0d2VlbiB0b3AgdXNlcnMgYW5kIG90aGVycy4gCiAqIEludGVyZXN0aW5nbHksIG5vIGJpZyBkaWZmZXJlbmNlcyBieSBkYXkgb2Ygd2VlawogKiBIb3cgaW50ZXJuYXRpb25hbCBpcyB0aGUgZm9ydW0/IAogCiMjIEFyZSB0aGVyZSBkaXN0aW5jdCBzdWJwb3B1bGF0aW9ucz8KIApgYGB7cn0KaW5jZWxfcG9zdHNfYyAlPiUgCiAgbXV0YXRlKGhvdXI9aG91cih0aW1lX3Bvc3RlZCkpICU+JQogIGNvdW50KHBvc3Rlcl9pZCxob3VyKSAlPiUKICBncm91cF9ieShwb3N0ZXJfaWQpICU+JQogIGZpbHRlcihzdW0obik+PTEwMCkgJT4lICMgbGltaXQgdG8gdXNlcnMgd2l0aCBlbm91Z2ggZGF0YSB0byBnZXQgYW55IHBhdHRlcm4KICBtdXRhdGUocHJvcG9ydGlvbj1uL3N1bShuKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGdncGxvdChhZXMoeD1ob3VyLHk9cHJvcG9ydGlvbikpICsKICBnZW9tX3F1YXNpcmFuZG9tKHNpemU9MC4yNSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoMCwwLjI1KSkgKwogIHRoZW1lX2hzY2lfZGlzY3JldGUoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5PTEpKSArCiAgeGxhYigiSG91ciAoVVRDKSIpICsKICB5bGFiKCJQcm9wb3J0aW9uIG9mIHBvc3RzIikgKwogIGxhYnMoY29sb3I9InVzZXIgdHlwZSIpICsKICBnZ3RpdGxlKCJQcm9wb3J0aW9uIG9mIHBvc3RzIGJ5IHRoZSB0aW1lIG9mIGRheSBmb3IgZWFjaCBpbmRpdmlkdWFsIHVzZXIiKQpgYGAKIAogICogVGhlcmUgZG8gbm90IHNlZW0gdG8gYmUgY2xlYXJseSBkaXN0aW5jdCB0aW1lIHByb2ZpbGVzIHdpdGggbGFyZ2UgZ3JvdXBzIG9mIHVzZXJzLiBUaGVyZSBtYXkgYmUgc29tZSB2YXJpYXRpb24gaW4gVVRDIG5pZ2h0IHRpbWUgcG9zdGluZyBiZWhhdmlvdXIgKDMtMTIgVVRDKQogIAojIEhvdyBsb25nIGFyZSBwZW9wbGUgYWN0aXZlIGJ5IHllYXIgam9pbmVkCgpgYGB7cn0KaW5jZWxfcG9zdHNfYyAlPiUKICAgIGdyb3VwX2J5KHBvc3Rlcl9pZCkgJT4lCiAgICBzdW1tYXJpc2UoZWFybGllc3RfcG9zdD1taW4odGltZV9wb3N0ZWQpLGxhdGVzdF9wb3N0PW1heCh0aW1lX3Bvc3RlZCksIC5ncm91cHM9ImRyb3AiKSAlPiUKICAgIG11dGF0ZShlYXJsaWVzdF9wb3N0X3llYXI9eWVhcihlYXJsaWVzdF9wb3N0KSwgYWN0aXZlX3BlcmlvZF9kYXlzPXNxbCgidGltZXN0YW1wZGlmZihkYXksZWFybGllc3RfcG9zdCxsYXRlc3RfcG9zdCkiKSkgJT4lCiAgZ2dwbG90KGFlcyh4PWVhcmxpZXN0X3Bvc3RfeWVhcix5PWFjdGl2ZV9wZXJpb2RfZGF5cykpICsgCiAgZ2VvbV9xdWFzaXJhbmRvbShzaXplPTAuMjUpICsKICB0aGVtZV9oc2NpX2Rpc2NyZXRlKCkgKwogIHhsYWIoIlllYXIgb2YgZWFybGllc3QgcG9zdCIpICsKICB5bGFiKCJUaW1lIGJldHdlZW4gZWFybGllc3QgYW5kIGxhdGVzdCBwb3N0IChkYXlzKSIpICsKICBnZ3RpdGxlKCJUaW1lIGJldHdlZW4gZWFybGllc3QgYW5kIGxhdGVzdCBwb3N0IGJ5IHllYXIgam9pbmVkIikKYGBgCgogKiBJbiAyMDE5LCB0aGVyZSBzZWVtIHRvIGhhdmUgYmVlbiBtb3JlIHBlb3BsZSBqb2luaW5nIHdobyBzdGF5ZWQgb24gbG9uZ2VyLgoKIyBEaWZmZXJlbnQgLWNlbHMKIApgYGB7cn0KY2VsX3Bvc3RfY29udGVudHMgPC0gaW5jZWxfcG9zdHNfYyAlPiUgCiAgZmlsdGVyKHN0cl9kZXRlY3QocG9zdF9jb250ZW50LCJjZWxzP1xcYiIpKSAlPiUgCiAgc2VsZWN0KHBvc3RfaWQsIHBvc3RfY29udGVudCkgJT4lIAogIGNvbGxlY3QoKSAKCmNlbHNfYnlfcG9zdCA8LSBjZWxfcG9zdF9jb250ZW50cyAlPiUKICBtdXRhdGUoY2VsPXN0cl9leHRyYWN0X2FsbChwb3N0X2NvbnRlbnQsICIoPzwhIGV4cGFuZC4uLilbXiBcXG5dKyBjZWxzP1xcYnxbXiBcXG5dKmNlbHM/XFxiKD8hIHNhaWQpIikpICU+JQogIHVubmVzdChjZWwpICU+JSAKICBmaWx0ZXIoIXN0cl9kZXRlY3QoY2VsLCAiXkAiKSkgJT4lCiAgc2VsZWN0KHBvc3RfaWQsIGNlbCkgJT4lIAogIG11dGF0ZShjZWw9Y2VsICU+JSAKICAgICAgICAgICBzdHJfdG9fbG93ZXIoKSAlPiUgCiAgICAgICAgICAgc3RyX3JlcGxhY2VfYWxsKCJcXFciLCIiKSAlPiUgCiAgICAgICAgICAgc3RyX3JlcGxhY2VfYWxsKCJzJCIsIiIpKQpgYGAKIAogCmBgYHtyfQpjZWxzIDwtIGNlbHNfYnlfcG9zdCAlPiUKICBjb3VudChjZWwpICU+JSAKICBhcnJhbmdlKGRlc2MobikpCmBgYAogCmBgYHtyfQpjZWxzICU+JSAKICB3cml0ZV90c3YoaGVyZSgiZGF0YS9vdXRwdXQvamllbWFrZWwvY2Vscy50c3YiKSxuYT0iIixxdW90ZT0ibmVlZGVkIikKYGBgCgpgYGB7cn0KY2VscyAlPiUKICBoZWFkKG49MTAwKSAlPiUKICBndChyb3duYW1lX2NvbD0iY2VsIikgJT4lCiAgZm10X2ludGVnZXIobikKYGBgCiAKYGBge3J9CmNlbHMyIDwtIGNlbHNfYnlfcG9zdCAlPiUKICBmaWx0ZXIoY2VsIT0iaW5jZWwiKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIGdyb3VwX2J5KHBvc3RfaWQpICU+JQogIGZpbHRlcihuKCk+MSkgJT4lCiAgYXJyYW5nZShjZWwpICU+JQogIHN1bW1hcmlzZShjZWw9c3RyX2ZsYXR0ZW4oY2VsLCBjb2xsYXBzZT0iLCAiKSwgLmdyb3Vwcz0iZHJvcCIpICU+JQogIGNvdW50KGNlbCkgJT4lIAogIGFycmFuZ2UoZGVzYyhuKSkKYGBgCiAKYGBge3J9CmNlbHMyICU+JSAKICB3cml0ZV90c3YoaGVyZSgiZGF0YS9vdXRwdXQvamllbWFrZWwvY2VsczIudHN2IiksbmE9IiIscXVvdGU9Im5lZWRlZCIpCmBgYAogCmBgYHtyfQpjZWxzMiAlPiUKICBoZWFkKG49MTAwKSAlPiUKZ3Qocm93bmFtZV9jb2w9ImNlbCIpICU+JQogIGZtdF9pbnRlZ2VyKG4pCmBgYAogCmBgYHtyfQp0cnVjZWxfcG9zdHMgPC0gY2Vsc19ieV9wb3N0ICU+JSAKICBmaWx0ZXIoc3RyX2RldGVjdChjZWwsInRydSIpKSAlPiUKICBkaXN0aW5jdChwb3N0X2lkKQoKZmFrZWNlbF9wb3N0cyA8LSBjZWxzX2J5X3Bvc3QgJT4lIAogIGZpbHRlcihzdHJfZGV0ZWN0KGNlbCwiZmFrZSIpKSAlPiUKICBkaXN0aW5jdChwb3N0X2lkKQpgYGAKIApgYGB7cn0KY2Vsc19ieV9wb3N0ICU+JSBpbm5lcl9qb2luKHRydWNlbF9wb3N0cywgam9pbl9ieShwb3N0X2lkKSkgJT4lCiAgZmlsdGVyKCFjZWwgJWluJSBjKCJ0cnVjZWwiLCJ0cnVlY2VsIiwgImluY2VsIiwgImh0dHBzaW5jZWwiKSkgJT4lCiAgY291bnQoY2VsKSAlPiUKICBhcnJhbmdlKGRlc2MobikpCgpjZWxzX2J5X3Bvc3QgJT4lIGlubmVyX2pvaW4oZmFrZWNlbF9wb3N0cywgam9pbl9ieShwb3N0X2lkKSkgJT4lCiAgZmlsdGVyKCFjZWwgJWluJSBjKCJmYWtlY2VsIiwiaW5jZWwiLCAiaHR0cHNpbmNlbCIpKSAlPiUKICBjb3VudChjZWwpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkKYGBgCgojIFlvdS93ZS90aGV5IGFyZQpgYGB7cn0KYXJlX3Bvc3RfY29udGVudHMgPC0gaW5jZWxfcG9zdHNfYyAlPiUgCiAgZmlsdGVyKHN0cl9kZXRlY3QocG9zdF9jb250ZW50LCIoeW91fHRoZXl8d2UpKCdyZXwgYXJlKSAiKSkgJT4lIAogIHNlbGVjdChwb3N0X2lkLCBwb3N0X2NvbnRlbnQpICU+JSAKICBjb2xsZWN0KCkgJT4lCiAgbXV0YXRlKHBvc3RfY29udGVudCA9IHBvc3RfY29udGVudCAlPiUgc3RyX3JlcGxhY2VfYWxsKCJDbGljayB0byBleHBhbmQuLi4iLCIuIikgJT4lIHN0cl9yZXBsYWNlX2FsbCgiXFxzKyIsIiAiKSkKYGBgCgoKYGBge3J9CmFyZXNfYnlfcG9zdCA8LSBjKDE6NCkgJT4lIAogIG1hcF9kZnIofmFyZV9wb3N0X2NvbnRlbnRzICU+JQogICAgbXV0YXRlKGxlbmd0aD0gLngsIGFyZT1zdHJfZXh0cmFjdF9hbGwocG9zdF9jb250ZW50LCBzdHJfYygiKHlvdXx0aGV5fHdlKSgncmV8IGFyZSkoJ250fCBub3QpPyggYXwgYW58IHRoZSk/Iiwgc3RycmVwKCIgXFx3KyIsLngpKSkpCiAgKSAlPiUgIAogIHVubmVzdChhcmUpICU+JQogIHNlbGVjdChwb3N0X2lkLCBhcmUsIGxlbmd0aCkgJT4lCiAgbXV0YXRlKGFyZT1hcmUgJT4lIHN0cl9yZXBsYWNlKCIncmUiLCIgYXJlIikgJT4lIHN0cl9yZXBsYWNlKCInbnQiLCAiIG5vdCIpKSAlPiUKICBtdXRhdGUoCiAgICB3aG89c3RyX3JlcGxhY2UoYXJlLCIgLioiLCIiKSwgCiAgICBhcmU9c3RyX3JlcGxhY2UoYXJlLCAiLio/ICIsIiIpLAogICAgc3RlbT1zdHJfcmVwbGFjZShhcmUsICIgW14gXSokIiwgIiIpCiAgKSAlPiUKICByZWxvY2F0ZShwb3N0X2lkLCBsZW5ndGgsIHdobywgYXJlKQphcmVzX2J5X3Bvc3QKYGBgCgoKYGBge3J9CmFyZXNfYnlfcG9zdF9jb3VudCA8LSBhcmVzX2J5X3Bvc3QgJT4lCiAgY291bnQobGVuZ3RoLCB3aG8sIGFyZSwgc3RlbSkKYXJlc19ieV9wb3N0X2NvdW50CmBgYAoKYGBge3J9CnRvcF9hcmVzIDwtIGFyZXNfYnlfcG9zdF9jb3VudCAlPiUgCiAgZ3JvdXBfYnkobGVuZ3RoLCB3aG8pICU+JQogIHNsaWNlX21heChuLG49MjApICU+JQogIHVuZ3JvdXAoKQpgYGAKCgpgYGB7cn0KYXJlc19ieV9wb3N0X2NvdW50ICU+JSAKICBhbnRpX2pvaW4odG9wX2FyZXMsIGpvaW5fYnkod2hvLCBhcmU9PXN0ZW0pKSAlPiUKICBhbnRpX2pvaW4oYXJlc19ieV9wb3N0X2NvdW50ICU+JSBtdXRhdGUobGVuZ3RoPWxlbmd0aCsxKSwgam9pbl9ieSh3aG8sYXJlLGxlbmd0aCkpICU+JQogIHNlbGVjdCgtc3RlbSkgJT4lCiAgZ3JvdXBfYnkobGVuZ3RoLCB3aG8pICU+JQogIHNsaWNlX21heChuLG49MjApICU+JQogIG11dGF0ZShvcmRlcj1yb3dfbnVtYmVyKCkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBmaWx0ZXIob3JkZXI8PTIwKSAlPiUKICBwaXZvdF93aWRlcihpZF9jb2xzPWMoImxlbmd0aCIsIm9yZGVyIiksIG5hbWVzX2Zyb209IndobyIsIHZhbHVlc19mcm9tPWMoImFyZSIsIm4iKSkgJT4lCiAgcmVsb2NhdGUoYXJlX3RoZXksbl90aGV5LGFyZV93ZSxuX3dlLGFyZV95b3Usbl95b3UpICU+JQogIGFycmFuZ2UoZGVzYyhsZW5ndGgpKSAlPiUKICBndChncm91cG5hbWVfY29sID0gImxlbmd0aCIsIHJvd25hbWVfY29sPSJvcmRlciIpICU+JQogIGNvbHNfbGFiZWwoCiAgICBhcmVfdGhleT0iVGhleSIsCiAgICBuX3RoZXk9Ik4iLAogICAgYXJlX3dlPSJXZSIsCiAgICBuX3dlPSJOIiwKICAgIGFyZV95b3U9IllvdSIsCiAgICBuX3lvdT0iTiIpICU+JQogIHRhYl9zdHlsZSgKICAgIHN0eWxlID0gbGlzdCgKICAgICAgY2VsbF9ib3JkZXJzKAogICAgICAgIHNpZGVzID0gYygicmlnaHQiKSwKICAgICAgICBzdHlsZSA9ICJzb2xpZCIKICAgICAgKQogICAgKSwKICAgIGxvY2F0aW9ucyA9IGNlbGxzX2JvZHkoCiAgICAgIGNvbHVtbnMgPSBjKG5fd2Usbl95b3Usbl90aGV5KQogICAgKQogICkKICAKYGBgCgo=