1) GIới thiệu ggplot2-Point graph

#Cài đặt và load ggplot2 
#install.packages('ggplot2')
library(ggplot2)
#x=1:100: trục x tạo ngẫu nhiên từ 1 đến 100
#y=rnorm (200,0,1) tạo dãy ngẫu nhiên gồm 200 số với giá trị trung bình là 0 và độ lệch chuẩn là 1
df <- data.frame(x=1:100,y = rnorm(200,0,1))
#Nếu chỉ dùng ggplot sẽ không có kết quả hiện ra
ggplot(df)

#Qui định kiểu đồ thị là các điểm point được khởi tạo trong df
ggplot(df) + geom_point(aes(x,y))

theme(): qui định theme cho đồ thị trong đó có nhiều kiểu theme khác nhau như:

. theme() hoặc theme_gray() là theme mặc định có phông nhạt và gridlines trắng.

. theme_bw() là kiểu đồ thị phông trắng có gridlines trắng.

. theme_classic() phông trắng không có gridlines.

. theme_minimal() phông tối giản không có trục tọa độ,.

. theme_dark() phông nền tối có gridlines.

. theme_void() phông nền empty.

. them_light() phông nền trắng với gridlines đen.

. theme_lightdraw() phông nền trắng với gridlines và axis xám.

Trong theme lại bao gồm 2 arguments đó là: base_size qui định font size của tiêu đề, base_family qui định kiểu chữ của các tiêu đề.

#Phông tối giản không có trục tọa độ
library(ggplot2)
df <- data.frame(x=1:100,y = rnorm(200,0,1))
ggplot(df) + geom_point(aes(x,y)) + theme_minimal()

#labs(): qui định các thành phần về title, subtitle, tên trục x, y
ggplot(df) + geom_point(aes(x,y)) + theme_minimal()+labs(title="First plot", subtitle = "Point Graph",x="x axis",y="y axis")

Scale theo gradient:

scale_*_gradient tạo thành 2 màu gradient (low-high),

scale_*_gradient2 tạo thành màu sắc phân tán thành (low- mid-high)

scale_*_gradientn tạo thành n-colour gradient.

Có thể tạo hệ màu sắc theo hàm terrain. Khi đó các mã hex của màu sắc sẽ được tạo ra tự động.

Lưu ý mã hex của màu sắc luôn bắt đầu bởi dấu # và gồm 6-8 kí tự. Các kí tự là chữ số phải nằm trong khoảng từ 0-9 hoặc chữ cái từ A-F.

#Tạo 5 màu tự động
terrain.colors(5,alpha = 0.2)
[1] "#00A60033" "#E6E60033" "#EAB64E33" "#EEB99F33" "#F2F2F233"
# Chọn màu sắc theo scale của cty
ggplot(mpg) +
geom_point(aes(hwy,displ, colour = cty)) +
scale_colour_gradientn(colours = terrain.colors(10))

Scale theo alpha

Các hàm có dạng:

  • scale_alpha(…, range = c(0.1, 1)): scale cho các biến liên tục
  • scale_alpha_continuous(…, range = c(0.1, 1)): scale cho các biến liên tục
  • scale_alpha_discrete(…, range = c(0.1, 1)): scale cho các biến phân loại
#Scale theo biến liên tục (year)
?geom_point
ggplot(mpg, aes(displ,hwy))+
  geom_point(aes(alpha = year)) + 
  scale_alpha(range = c(0.2,1))

#Scale theo biến phân loại (class)
ggplot(mpg, aes(displ,hwy))+
geom_point(aes(alpha = class)) +
scale_alpha_discrete(range=c(0.2,1))
Using alpha for a discrete variable is not advised.

Scale theo colour

scale_colour_brewer(…, type = “seq”, palette = 1, direction = 1)

scale_fill_brewer(…, type = “seq”, palette = 1, direction = 1)

scale_colour_distiller(…, type = “seq”, palette = 1, direction = -1, values = NULL, space = “Lab”, na.value = “grey50”, guide = “colourbar”)

scale_fill_distiller(…, type = “seq”, palette = 1, direction = -1, values = NULL, space = “Lab”, na.value = “grey50”, guide = “colourbar”)

Đối với các biểu đồ dạng rectangle ta sẽ như barchart hay heatmap ta phải dùng scale_fill, còn các biểu đồ của point ta sẽ dùng scale_colour.

Trong đó :

…: là các argument khác để định nghĩa names, limits, breaks, labels.

type: là 3 kiểu lựa chọn sequential, diverging, qualitative.

palette: tên của bảng màu, có thể là string hoặc index.

direction: thứ tự các màu trong bảng màu, 1 là thứ tự tăng dần, -1 là ngược chiều.

values: Đánh số thứ tự trong vector gradient, hầu hết để NULL

space: Không gian màu sắc để từ đó tính toán gradient, nên chọn là “Lab” vì những space khác đã depreciated.

na.value: màu sử dụng cho missing value.

guide: Kiểu đánh màu gồm “colourbar” cho các màu liên tục, “legend” cho các màu phân loại.

#màu sắc mặc định theo class
(p <- ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(colour = class)))

#scale theo color brewer
p + scale_colour_brewer("Class of ܹn cars")

#scale sử dụng palettes Accent của Qualitative
p + scale_color_brewer(palette = "Accent")

#scale sử dụng palettes Set1 của sequential 
p + scale_color_brewer(palette = "Set1")

#Scale trong biểu đồ histogram
(h <- ggplot(diamonds, aes(x=price, fill=cut))+
    geom_histogram(position = "dodge", binwidth = 1500))

#Lưu ý đối với biểu đồ barchart ta phải dùng scale_fill thay cho scale_colour
h + scale_fill_brewer()

#Đảo ngược vị trí màu sắc
h + scale_fill_brewer(direction = -1)

#Sử dụng scale_fill trong heatmap
(v <- ggplot(faithfuld) + geom_tile(aes(waiting, eruptions, fill
= density)))

#Scale theo màu sắc bằng nhóm màu phân tán Spectral của palatte Diverging.
v + scale_fill_distiller(palette = "Spectral")

#Scale theo biến phân loại class
ggplot(mpg, aes(displ, hwy)) + 
  geom_point(aes(colour = class))

Scale theo thời gian

scale_*_date: scale time cho các kiểu thời gian thuộc class Date

scale_*_datetime: dành cho các class POSIXct

scale_*_time: dành có các kiểu thời gian thuộc class hms

Cú pháp chung:

scale_y_datetime(name = waiver(), breaks = waiver(), date_breaks = waiver(), labels = waiver(), date_labels = waiver(), minor_breaks = waiver(), date_minor_breaks = waiver(), timezone = NULL, limits = NULL, expand = waiver(), position = “left”)

Trong đó:

break: Khoảng break là bao nhiêu, có thể nhận các giá trị NULL (không break), waiver() theo mặc định package tính toán, một numeric vector của position, một hàm số trả về các khoảng breaks.

date_break: Xác định khoảng thời gian giữa các bước liên tiếp là bao nhiêu như “2 weeks”, “10 years”,…

labels: Là một trong những giá trị NULL (không có labels), waiver() là default label của package tính ra, một character vector gán nhãn cho labels(phải có độ dài = breaks), hoặc một function trả về labels.

date_labels: Là kí tự định dạng format thời gian cho labels. VD “%Y %b”, code này phải tuân theo kiểu strftime. Nếu labels được định dạng cùng với date_labels thì date_labels thắng.

limits: là vector cung cấp độ dài của scale gồm 2 điểm đầu và cuối. Chẳng hạn như c(as.Date(“2018-04-21”)-7,NA), c(as.Date(“2018-04-21”),as.Date(“2019-04-21”))

# Chứa giá trị date time 29 ngày trước
last_month <- Sys.Date() - 0:29
#runif tạo ra 30 giá trị độ lệch chuẩn random
df <- data.frame( date = last_month, price = runif(30) )
#Vẽ biểu đồ với định dạng time là yyyy-mm-dd
#scale time cho các kiểu thời gian thuộc class Date
ggplot(df,aes(date,price))+ geom_line() +
scale_x_date(date_labels = "%Y-%m-%d")

#Khai báo khoảng thời gian của các bước liên tiếp (date_breaks) là 1 tuần
# date_labels dạng format là tuần
ggplot(df,aes(date,price))+
  geom_line() + 
  scale_x_date(date_breaks = "1 week",date_labels = "%W")

#Chuyển sang date break = 1 ngày
#date_labels theo format là ngày
ggplot(df,aes(date,price))+ geom_line() +
scale_x_date(date_breaks = "1 day",date_labels = "%d")

#Datebreak = 1 ngày, date limit là bắt đầu từ 7 ngày trước cho tới nay
ggplot(df,aes(date,price))+ geom_line() +scale_x_date(date_breaks = "1 day",date_labels = "%Y-%b-
%d",limits = c(Sys.Date()-7,NA))

Chia đồ thị thành nhiều facet

2 hàm facet_wrap() và facet_grid() sử dụng để phân chia 1 đồ thị thành nhiều facet khác nhau. Trong đó:

facet_wrap(): phân chia 1 mảnh đồ thị thành nhiều mảnh facet khác nhau. Nó được sử dụng phổ biến hơn so với facet_grid() vì các hiểu thị đều đưa ra kết quả hình chữ nhật. Cú pháp:

facet_wrap(facets, nrow = NULL, ncol = NULL, scales = “fixed”, shrink = TRUE, labeller = “label_value”, as.table = TRUE, drop = TRUE, dir = “h”, strip.position = “top”)

Trong đó một số argument chính:

  • facets: là công thức hoặc vector. Khi là công thức thì chỉ có 1 side chẳng hạn ~a+b, khi là vector sẽ có dạng c(“a”,“b”) qui định chiều sử dụng để phân loại dữ liệu thành các nhóm và mỗi nhóm sẽ được vẽ trên 1 facet.

  • nrow, ncol: qui định số dòng, số cột để sắp xếp các facets.

labeller: xác định label được hiển thị cho mỗi facet. Thông thường sẽ dùng “label_both”

  • scales: scales ở các trục x,y được fixed, free hoặc free 1 trong 2 trục với free_x hoặc free_y

  • strip.position: thay đổi vị trí các label. Có các option (“top”,“bottom”,“left”,“right”)

  • dir: thay đổi chiều biểu diễn các facets là dọc hay ngang dựa trên lựa chọn “v” hoặc “h”.

#Tạo mọt facet với các nhóm phân loại dựa trên class
ggplot(mpg) + geom_point(aes(hwy, displ)) +
facet_wrap(c("class"))

#Xác định label  hiển thị mỗi facet
ggplot(mpg) + geom_point(aes(hwy, displ)) +
facet_wrap(c("class"), labeller = "label_both")

#Thay đổi vị trí của các label xuống dưới
ggplot(mpg) + geom_point(aes(hwy, displ)) +
facet_wrap(c("class"), labeller = "label_both",
strip.position = "bottom")

#Thay đổi chiều hiển thị facets từ ngang sang dọc sử dụng dir="v"
ggplot(mpg) + 
  geom_point(aes(hwy, displ)) + 
  facet_wrap(c("class"), dir = "v", strip.position = "bottom")

#Các facet đang có scale fixed(cố định), chúng ta muốn cho trục y free tức là mỗi một facet sẽ có một scale riêng 
ggplot(mpg) + geom_point(aes(hwy, displ)) +
facet_wrap(c("class"), dir = "v", strip.position = "bottom",
scales = "free_y")

#Nếu muốn các đồ thị lặp lại dữ liệu và chỉ hightlight các category thuộc facet đó thì phải tạo một facet không chứa facet variable
#sử dụng hàm transform để tạo một facet mà có class= Null tức là không chứa các category thuộc facet.
ggplot(mpg,aes(displ, hwy)) + geom_point(data = transform(mpg, class
= NULL), colour = "grey") + geom_point() + facet_wrap(c("class"),
dir = "v", strip.position = "bottom", scales = "free_y")

#Bản chất đồ thị trên gồm 2 lớp, lớp đầu tiên là tô cho toàn bộ đồ thịmàu grey, bước thứ 2 là ở geom_point() bên dưới, chỉ những điểm có category thuộc facet mới được tô màu đè lên lớp 1. 
#Tô màu theo scale thật sặc sỡ
# Sử dụng scale theo gradient và tạo 10 màu tự động bằng hàm terrain
ggplot(mpg,aes(displ, hwy)) + geom_point(aes(colour = hwy)) +
facet_wrap(c("class"), strip.position = "bottom") +
scale_color_gradientn(colours = terrain.colors(10,alpha = 0.4))

#Điều chỉnh thêm theme theo màu tối bằng theme_dark() cho dễ nhìn
ggplot(mpg,aes(displ, hwy)) + geom_point(aes(colour = hwy)) +
theme_dark() + facet_wrap(c("class"), strip.position = "bottom") +
scale_color_gradientn(colours = terrain.colors(10,alpha = 0.4))

Thiết lập giới hạn cho Scale

lims(): thiết lập giới hạn cho các category xlim(): Thiết lập giới hạn cho trục x ylim(): Thiết lập giới hạn cho trục y

#Đồ thị gốc
ggplot(mpg) +
  geom_point(aes(hwy, displ))

#Thiết lập giới hạn cho x từ 15-20 sử dụng xlim(15,20)
ggplot(mpg) + geom_point(aes(hwy, displ)) + xlim(15,20)

#Thiết lập giới hạn cho y từ 3-7
ggplot(mpg) +
  geom_point(aes(hwy, displ)) + 
  ylim(3,7)

#Thiết lập giới hạn cho class thuộc các nhóm c("compact","midsize","suv","minivan")
# Giới hạn 4 class
ggplot(mpg) + geom_point(aes(hwy, displ, colour = class)) +
lims(colour = c("compact","midsize","suv","minivan"))

2) GIới thiệu ggplot2-Line graph

# Sử dụng dataset pressure
df <- pressure
head(df)
# Tạo line graph sử dụng hàm ggplot()
library(ggplot2)
ggplot(df, aes(x=temperature, y=pressure)) + geom_line()

# giới hạn vùng cho trục tọa độ sử sụng xlim và ylim
# Ở đây giới hạn y trong khoảng từ 200 cho tới y max
ggplot(df, aes(x=temperature, y=pressure)) + geom_line() +
ylim(200, max(pressure))

# Sử dụng xlim giới hạn x trong khoảng 100 tới 500
ggplot(df, aes(x=temperature, y=pressure)) + geom_line() +
xlim(100, 500)

# Thêm points nằm trên line graph bằng geom_point()
ggplot(df, aes(x=temperature, y=pressure)) +
geom_line() + geom_point()

# Với log y-axis, y được biểu diễn theo dạng y=log10(x)
ggplot(df, aes(x=temperature, y=pressure)) +
geom_line() + geom_point() + scale_y_log10()

Tạo line graph với multiple lines

# Tạo data frames
nmonths = 24
#Tạo ra 1 mảng tăng dần bắt đầu từ tháng 1/2015 và tăng dần theo tháng với số lượng phần từ của mảng là 24.
x = seq(as.Date("2015/1/1"), by = "month", length.out = nmonths)
# rnorm(mean= ,nmonths) tạo ra 1 vector gồm 24 giá trị random với giá trị trung bình tương ứng
df1 <- data.frame(dates = x,Variable = rnorm(mean = 0.75,nmonths))
df2 <- data.frame(dates = x,Variable = rnorm(mean = -0.75,nmonths))
df3 <- data.frame(dates = x,Variable = rnorm(mean = 0.3,nmonths))
#Hiển thị datafram 1, df1 có kích thước 24x2
head(df1)
##df3 cũng có kích thước 24x2, với giá trị random trung bình =0.3
head(df3)
# Tạo biểu đồ đường với nhiều dòng có màu khác nhau tương ứng với 3 data fram được tạo ở trên
library(ggplot2)
p <- ggplot() + geom_line(data = df1, aes(x = dates, y = Variable),
color = "blue") + geom_line(data = df2, aes(x = dates, y = Variable),
color = "red") + geom_line(data = df3, aes(x = dates, y = Variable),
color = "green")
print(p)

#Cài đặt và sử dụng dplyr
install.packages("dplyr")
library(dplyr)
# Tạo category cho 3 data frames và gộp chúng lại sử dụng dplyr
# %>% dùng để truyền dataframe  vào 1 hàm, bind_rows dùng để gộp cái dataframe với nhau, mutate dùng để tạo thêm 1 cột trong mỗi data frame và sử dụng những chữ cái A,B,C để giúp phân biệt df1,df2,df3 
library(dplyr)
df <- df1 %>% mutate(cat = "A") %>% bind_rows(df2 %>%
mutate(cat = "B")) %>% bind_rows(df3 %>% mutate(cat =
"C"))
head(df)
# Tạo multiple lines graphs mỗi màu sắc tương ứng với thuộc tính cat vừa được tạo ở trên, 3 màu tương ứng với mỗi dataframe
ggplot(df, aes(x = dates, y = Variable, color =
cat)) + geom_line()

Thay đổi hình dạng của line

#Thay đổi hình dạng của line với màu xanh và đứt quãng 
ggplot(df1, aes(x = dates, y = Variable)) +  geom_line(linetype="dashed", size=1, colour="blue")

Thay đổi hình dạng của points

#Thêm những điểm màu hồng có kích thước =4 và hình dạng 22
ggplot(df1, aes(x = dates, y = Variable)) +  
  geom_line() +
  geom_point(size=4, shape=22, colour="darkred", fill="pink")

Tạo Graph with a Shaded Area

#Tạo bóng lên 1 vùng phủ bằng geom_area()
ggplot(df1, aes(x = dates, y = Variable)) +  
  geom_line() +
  geom_area()

ggplot(df1, aes(x = dates, y = Variable)) + geom_line() +
# Tạo 80% trong suốt với thiết lập alpha bằng 0.2
# Việc này giúp cho chúng ta vẫn thấy những đường lưới hiển thị phía sau lớp phủ xanh
geom_area(colour="black", fill="green", alpha=.2)

# Thử với alpha = 0.8
# Độ trong suốt chỉ với 20% nên những đừng kẻ mờ hoàn toàn bị vùng phủ che khuất
ggplot(df1, aes(x = dates, y = Variable)) + geom_line() +
geom_area(colour="black", fill="green", alpha=.8)

Tạo những vùng phủ xếp chồng lên nhau

#Hình hiển thị cho thấy 3 data frame A,B,C xếp chồng lên nhau bằng 3 màu phủ khác nhau 
ggplot(df, aes(x = dates, y = Variable, fill = cat)) +
  geom_area()

# Thiết lập độ trong suốt =0.6 với alpha=0.4 với khu vực phủ theo màu tối
#scale theo bảng màu pallete màu xanh, với khoảng cách giữa 2 bước liên tiếp tương ứng việc chuyển đổi dataframe này sang dataframe khác theo loại cat.
ggplot(df, aes(x = dates, y = Variable, fill = cat)) +
geom_area(colour="black", size=.2, alpha=.4) +
scale_fill_brewer(palette="Blues", breaks=rev(levels(cat)))

?rev

Thêm confidence region

# Tính 95% confidence interval cho variable
# Viết hàm tính CI
confidence_interval <- function(vector, interval) {
  # Độ lệch chuẩn của sample
  vec_sd <- sd(vector)
  # Sample size
  n <- length(vector)
  # Giá trị trung bình của sample
  vec_mean <- mean(vector)
  # Lỗi dựa vào sự phân bố t tính bằng cách sử dụng hàm qt
  error <- qt((interval + 1)/2, df = n - 1) * vec_sd / sqrt(n)
  # Confidence interval as a vector
  # result <- data.frame("lower" = vec_mean - error, "upper" = vec_mean + error)
  # Tuy nhiên để tạo ra hai cột lower và upper cho vector variable tôi làm như sau:
  result <- data.frame("lower" = vector - error, "upper" = vector + error)
  return(result)
}
#Ví dụ
vector <- c(12, 17, 24, 35, 23, 34, 56)
confidence_interval(vector, 0.90)
# Tính confidence region với tham số vector truyền vào là thuộc Variable của df1 
#Kết hợp df1 với confidence region vừa tính xong và thành 1 bảng df mới với 2 thuộc tính mới lower và upper được thêm vào
library(magrittr)
range <- confidence_interval(df1$Variable, 0.95)
df1 <- cbind(df1, range)
head(df1)
#Sử dụng geom_ribbon() để nối những giá trị cho ymin and ymax tương ứng với confidence region được tính ở trên, đồng thời để tạo vùng phủ với ymax ymin tương ứng.

ggplot(df1, aes(x = dates, y= Variable)) +
geom_ribbon(aes(ymin = lower, ymax = upper), alpha=0.2) +
geom_line()

?geom_ribbon
# Sử dụng đường chấm đốm dể tạo ra ranh giới cho upper và lower
ggplot(df1, aes(x=dates, y=Variable)) +
  geom_line(aes(y=lower), colour="grey50", linetype="dotted") +
  geom_line(aes(y=upper), colour="grey50", linetype="dotted") +
  geom_line()

Dữ liệu chuỗi thời gian

library(ggplot2)
# Dữ liệu demo: economics trong gói ggplot2
head(economics)

Tạo line plots cơ bản:

# Plot một tập con của data
# Tập con này bao gồm những dòng trong economics mà có date > 2006-1-1 
ss <- subset(economics, date > as.Date("2006-1-1"))
ggplot(data = ss, aes(x = date, y = pop)) +
geom_line(color = "#FC4E07", size = 2)

#Điều chỉnh kích thước line với việc chỉnh size= thương 2 thuộc tính
ggplot(data = economics, aes(x = date, y = pop)) +
geom_line(aes(size = unemploy/pop), color = "#FC4E07")

Tạo multiple time series plots

#Cài đặt tidyr và sử dụng
install.packages("tidyr")
library(tidyr)
# Để tạo multiple plot bởi 2 biến psavert và uempmed theo dates. Đầu tiên cần định hình lại data sửu dụng tidyr package
library(tidyr)
library(dplyr)
# chọn ra thuộc tính date,psavert, uempmed trong economics tương ứng với kiểu thuộc tính variable, value, date
df <- economics %>%
  select(date, psavert, uempmed) %>%
  gather(key = "variable", value = "value", -date)
head(df, 3)
# Multiple line plot
# Plot 2 thuộc tính psavert và uempmed theo 2 màu khác nhau tuỳ chọn trong theme tối thiểu không có trục toạ độ
ggplot(df, aes(x = date, y = value)) +
geom_line(aes(color = variable), size = 1) +
scale_color_manual(values = c("#00AFBB", "#E7B800")) +
theme_minimal()

# plot những vùng phủ
# Plot nhiều vùng chồng chéo lên, do đây biểu đồ dạng heatmap nên ta sử dụng scale_fill_manual, plot 2 vùng chồng chéo theo 2 máu khác nhau
ggplot(df, aes(x = date, y = value)) +
geom_area(aes(color = variable, fill = variable),
alpha = 0.5, position = position_dodge(0.8)) +
scale_color_manual(values = c("#00AFBB", "#E7B800")) +
scale_fill_manual(values = c("#00AFBB", "#E7B800"))

?geom_area()

Set date axis limits

# Base plot with date axis
#Biểu đồ xu hướng  theo ngày
p <- ggplot(data = economics, aes(x = date, y = psavert)) + 
     geom_line(color = "#00AFBB", size = 1)
p

# Set axis limits c(min, max)
#Thiết lập biểu đồ cột x xuất pháp từ ngày nhỏ nhất nhất là 2002-1-1 và lớn nhất là cho tới hiện tại
min <- as.Date("2002-1-1")
max <- NA
p+ scale_x_date(limits = c(min, max))

Định dạngt date axis labels

# Thiết lập theo định dạng tháng/năm cho thuộc tính datetime
p + scale_x_date(date_labels = "%b/%Y")

Add trend smoothed line

# Thêm 1 dòng biểu diễn xu hướng của biểu đồ với phương thức loess
p + stat_smooth(
  color = "#FC4E07", fill = "#FC4E07",
  method = "loess"
  )

ggfortify- ggpmisc

  • ggfortify là package mở rộng của ggplot2

ggfortify vẽ biểu đồ chuỗi thời gian (plot time series objects) zoo::zooreg(), xts::xts(), timeSeries::timSeries(), tseries::irts(), forecast::forecast(), vars:vars().

  • ggpmisc package: cung cấp 2 phương pháp cho time series object:

stat_peaks() finds at which x positions local y maxima are located,and

stat_valleys() finds at which x positions local y minima are located.

#Cài đặt
install.packages( c("ggfortify", "changepoint", "strucchange",
"ggpmisc") )
# Load thư viện
library(ggfortify)
library(magrittr) # for piping %>%
# Sử dụng hàm  auplot để khắc hoạ đối tượng chuỗi thời gian 
autoplot(AirPassengers)

# Phát hiện ra những điểm thay đổi dựa trên trung bình và phương sai trong df AirPassengers
AirPassengers %>%
  changepoint:: cpt.meanvar() %>%  # Identify change points
  autoplot()

# Phát hiện ra những bước nhảy trong data
strucchange::breakpoints(Nile ~ 1) %>% autoplot()

# Phát hiện những điểm cao nhất theo y(peaks) và thấp nhất theo y(valleys)
#Những điểm cao nhất tương ứng màu đỏ và thấp nhất tương ứng màu xanh
#Cột x theo thuộc tính datetime là năm, và y theo thuộc tính lynx, những điểm thấp nhất sẽ tạo thành 1 góc 45 độ
library(ggpmisc)
ggplot(lynx, as.numeric = FALSE) + geom_line() +
stat_peaks(colour = "red") +
stat_peaks(geom = "text", colour = "red", vjust = -0.5,
x.label.fmt = "%Y") +
stat_valleys(colour = "blue") +
stat_valleys(geom = "text", colour = "blue", angle = 45,
vjust = 1.5, hjust = 1, x.label.fmt = "%Y")+
ylim(-500, 7300)

LS0tDQp0aXRsZTogIkLDoGkgdOG6rXAgdHXhuqduIDJfR2nhu5tpIHRoaeG7h3UgZ2dwbG90MiINCmF1dGhvcjogMDhfTWFpIEh1eV80My4wMS4xMDQuMDY1DQpkYXRlOiAyMi80LzIwMjANCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KDQotLS0NCg0KDQoNCiMgMSkgR0nhu5tpIHRoaeG7h3UgZ2dwbG90Mi1Qb2ludCBncmFwaA0KDQpgYGB7cn0NCiNDw6BpIMSR4bq3dCB2w6AgbG9hZCBnZ3Bsb3QyIA0KI2luc3RhbGwucGFja2FnZXMoJ2dncGxvdDInKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KYGBgDQpgYGB7cn0NCiN4PTE6MTAwOiB0cuG7pWMgeCB04bqhbyBuZ+G6q3Ugbmhpw6puIHThu6sgMSDEkeG6v24gMTAwDQojeT1ybm9ybSAoMjAwLDAsMSkgdOG6oW8gZMOjeSBuZ+G6q3Ugbmhpw6puIGfhu5NtIDIwMCBz4buRIHbhu5tpIGdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggbMOgIDAgdsOgIMSR4buZIGzhu4djaCBjaHXhuqluIGzDoCAxDQpkZiA8LSBkYXRhLmZyYW1lKHg9MToxMDAseSA9IHJub3JtKDIwMCwwLDEpKQ0KI07hur91IGNo4buJIGTDuW5nIGdncGxvdCBz4bq9IGtow7RuZyBjw7Mga+G6v3QgcXXhuqMgaGnhu4duIHJhDQpnZ3Bsb3QoZGYpDQojUXVpIMSR4buLbmgga2nhu4N1IMSR4buTIHRo4buLIGzDoCBjw6FjIMSRaeG7g20gcG9pbnQgxJHGsOG7o2Mga2jhu59pIHThuqFvIHRyb25nIGRmDQpnZ3Bsb3QoZGYpICsgZ2VvbV9wb2ludChhZXMoeCx5KSkNCmBgYA0KDQp0aGVtZSgpOiBxdWkgxJHhu4tuaCB0aGVtZSBjaG8gxJHhu5MgdGjhu4sgdHJvbmcgxJHDsyBjw7Mgbmhp4buBdSBraeG7g3UgdGhlbWUga2jDoWMgbmhhdSBuaMawOg0KDQouIHRoZW1lKCkgaG/hurdjIHRoZW1lX2dyYXkoKSBsw6AgdGhlbWUgbeG6t2MgxJHhu4tuaCBjw7MgcGjDtG5nIG5o4bqhdCB2w6AgZ3JpZGxpbmVzIHRy4bqvbmcuDQoNCi4gdGhlbWVfYncoKSBsw6Aga2nhu4N1IMSR4buTIHRo4buLIHBow7RuZyB0cuG6r25nIGPDsyBncmlkbGluZXMgdHLhuq9uZy4NCg0KLiB0aGVtZV9jbGFzc2ljKCkgcGjDtG5nIHRy4bqvbmcga2jDtG5nIGPDsyBncmlkbGluZXMuDQoNCi4gdGhlbWVfbWluaW1hbCgpIHBow7RuZyB04buRaSBnaeG6o24ga2jDtG5nIGPDsyB0cuG7pWMgdOG7jWEgxJHhu5ksLg0KDQouIHRoZW1lX2RhcmsoKSBwaMO0bmcgbuG7gW4gdOG7kWkgY8OzIGdyaWRsaW5lcy4NCg0KLiB0aGVtZV92b2lkKCkgcGjDtG5nIG7hu4FuIGVtcHR5Lg0KDQouIHRoZW1fbGlnaHQoKSBwaMO0bmcgbuG7gW4gdHLhuq9uZyB24bubaSBncmlkbGluZXMgxJFlbi4NCg0KLiB0aGVtZV9saWdodGRyYXcoKSBwaMO0bmcgbuG7gW4gdHLhuq9uZyB24bubaSBncmlkbGluZXMgdsOgIGF4aXMgeMOhbS4NCg0KVHJvbmcgdGhlbWUgbOG6oWkgYmFvIGfhu5NtIDIgYXJndW1lbnRzIMSRw7MgbMOgOiBiYXNlX3NpemUgcXVpIMSR4buLbmggZm9udCBzaXplIGPhu6dhIHRpw6p1IMSR4buBLCBiYXNlX2ZhbWlseSBxdWkgxJHhu4tuaCBraeG7g3UgY2jhu68gY+G7p2EgY8OhYyB0acOqdSDEkeG7gS4NCg0KDQpgYGB7cn0NCiNQaMO0bmcgdOG7kWkgZ2nhuqNuIGtow7RuZyBjw7MgdHLhu6VjIHThu41hIMSR4buZDQpsaWJyYXJ5KGdncGxvdDIpDQpkZiA8LSBkYXRhLmZyYW1lKHg9MToxMDAseSA9IHJub3JtKDIwMCwwLDEpKQ0KZ2dwbG90KGRmKSArIGdlb21fcG9pbnQoYWVzKHgseSkpICsgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KYGBge3J9DQojbGFicygpOiBxdWkgxJHhu4tuaCBjw6FjIHRow6BuaCBwaOG6p24gduG7gSB0aXRsZSwgc3VidGl0bGUsIHTDqm4gdHLhu6VjIHgsIHkNCmdncGxvdChkZikgKyBnZW9tX3BvaW50KGFlcyh4LHkpKSArIHRoZW1lX21pbmltYWwoKStsYWJzKHRpdGxlPSJGaXJzdCBwbG90Iiwgc3VidGl0bGUgPSAiUG9pbnQgR3JhcGgiLHg9InggYXhpcyIseT0ieSBheGlzIikNCmBgYA0KDQojIyBTY2FsZSB0aGVvIGdyYWRpZW50Og0KDQpzY2FsZV8qX2dyYWRpZW50IHThuqFvIHRow6BuaCAyIG3DoHUgZ3JhZGllbnQgKGxvdy1oaWdoKSwNCg0Kc2NhbGVfKl9ncmFkaWVudDIgdOG6oW8gdGjDoG5oIG3DoHUgc+G6r2MgcGjDom4gdMOhbiB0aMOgbmggKGxvdy0NCm1pZC1oaWdoKQ0KDQpzY2FsZV8qX2dyYWRpZW50biB04bqhbyB0aMOgbmggbi1jb2xvdXIgZ3JhZGllbnQuDQoNCkPDsyB0aOG7gyB04bqhbyBo4buHIG3DoHUgc+G6r2MgdGhlbyBow6BtIHRlcnJhaW4uIEtoaSDEkcOzIGPDoWMgbcOjIGhleA0KY+G7p2EgbcOgdSBz4bqvYyBz4bq9IMSRxrDhu6NjIHThuqFvIHJhIHThu7EgxJHhu5luZy4NCg0KTMawdSDDvSBtw6MgaGV4IGPhu6dhIG3DoHUgc+G6r2MgbHXDtG4gYuG6r3QgxJHhuqd1IGLhu59pIGThuqV1ICMgdsOgIGfhu5NtIDYtOA0Ka8OtIHThu7EuIEPDoWMga8OtIHThu7EgbMOgIGNo4buvIHPhu5EgcGjhuqNpIG7hurFtIHRyb25nIGtob+G6o25nIHThu6sgMC05IGhv4bq3Yw0KY2jhu68gY8OhaSB04burIEEtRi4NCmBgYHtyfQ0KI1ThuqFvIDUgbcOgdSB04buxIMSR4buZbmcNCnRlcnJhaW4uY29sb3JzKDUsYWxwaGEgPSAwLjIpDQpgYGANCg0KYGBge3J9DQojIENo4buNbiBtw6B1IHPhuq9jIHRoZW8gc2NhbGUgY+G7p2EgY3R5DQpnZ3Bsb3QobXBnKSArDQpnZW9tX3BvaW50KGFlcyhod3ksZGlzcGwsIGNvbG91ciA9IGN0eSkpICsNCnNjYWxlX2NvbG91cl9ncmFkaWVudG4oY29sb3VycyA9IHRlcnJhaW4uY29sb3JzKDEwKSkNCmBgYA0KDQojIyBTY2FsZSB0aGVvIGFscGhhDQoNCkPDoWMgaMOgbSBjw7MgZOG6oW5nOg0KDQogLSBzY2FsZV9hbHBoYSguLi4sIHJhbmdlID0gYygwLjEsIDEpKTogc2NhbGUgY2hvIGPDoWMgYmnhur9uIGxpw6puIHThu6VjDQogLSBzY2FsZV9hbHBoYV9jb250aW51b3VzKC4uLiwgcmFuZ2UgPSBjKDAuMSwgMSkpOiBzY2FsZSBjaG8gY8OhYyBiaeG6v24gbGnDqm4gdOG7pWMNCiAtIHNjYWxlX2FscGhhX2Rpc2NyZXRlKC4uLiwgcmFuZ2UgPSBjKDAuMSwgMSkpOiBzY2FsZSBjaG8gY8OhYyBiaeG6v24gcGjDom4gbG/huqFpDQogDQpgYGB7cn0NCiNTY2FsZSB0aGVvIGJp4bq/biBsacOqbiB04bulYyAoeWVhcikNCj9nZW9tX3BvaW50DQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsaHd5KSkrDQogIGdlb21fcG9pbnQoYWVzKGFscGhhID0geWVhcikpICsgDQogIHNjYWxlX2FscGhhKHJhbmdlID0gYygwLjIsMSkpDQpgYGANCg0KYGBge3J9DQojU2NhbGUgdGhlbyBiaeG6v24gcGjDom4gbG/huqFpIChjbGFzcykNCmdncGxvdChtcGcsIGFlcyhkaXNwbCxod3kpKSsNCmdlb21fcG9pbnQoYWVzKGFscGhhID0gY2xhc3MpKSArDQpzY2FsZV9hbHBoYV9kaXNjcmV0ZShyYW5nZT1jKDAuMiwxKSkNCmBgYA0KDQojIyBTY2FsZSB0aGVvIGNvbG91cg0Kc2NhbGVfY29sb3VyX2JyZXdlciguLi4sIHR5cGUgPSAic2VxIiwgcGFsZXR0ZSA9IDEsIGRpcmVjdGlvbiA9IDEpDQoNCnNjYWxlX2ZpbGxfYnJld2VyKC4uLiwgdHlwZSA9ICJzZXEiLCBwYWxldHRlID0gMSwgZGlyZWN0aW9uID0gMSkNCg0Kc2NhbGVfY29sb3VyX2Rpc3RpbGxlciguLi4sIHR5cGUgPSAic2VxIiwgcGFsZXR0ZSA9IDEsIGRpcmVjdGlvbiA9IC0xLA0KICB2YWx1ZXMgPSBOVUxMLCBzcGFjZSA9ICJMYWIiLCBuYS52YWx1ZSA9ICJncmV5NTAiLA0KICBndWlkZSA9ICJjb2xvdXJiYXIiKQ0KDQpzY2FsZV9maWxsX2Rpc3RpbGxlciguLi4sIHR5cGUgPSAic2VxIiwgcGFsZXR0ZSA9IDEsIGRpcmVjdGlvbiA9IC0xLA0KICB2YWx1ZXMgPSBOVUxMLCBzcGFjZSA9ICJMYWIiLCBuYS52YWx1ZSA9ICJncmV5NTAiLA0KICBndWlkZSA9ICJjb2xvdXJiYXIiKQ0KICANCsSQ4buRaSB24bubaSBjw6FjIGJp4buDdSDEkeG7kyBk4bqhbmcgcmVjdGFuZ2xlIHRhIHPhur0gbmjGsCBiYXJjaGFydCBoYXkgaGVhdG1hcCB0YSBwaOG6o2kgZMO5bmcgc2NhbGVfZmlsbCwgY8OybiBjw6FjIGJp4buDdSDEkeG7kyBj4bunYSBwb2ludCB0YSBz4bq9IGTDuW5nIHNjYWxlX2NvbG91ci4NCg0KVHJvbmcgxJHDsyA6DQoNCuKApjogbMOgIGPDoWMgYXJndW1lbnQga2jDoWMgxJHhu4MgxJHhu4tuaCBuZ2jEqWEgbmFtZXMsIGxpbWl0cywgYnJlYWtzLCBsYWJlbHMuDQoNCnR5cGU6IGzDoCAzIGtp4buDdSBs4buxYSBjaOG7jW4gc2VxdWVudGlhbCwgZGl2ZXJnaW5nLCBxdWFsaXRhdGl2ZS4NCg0KcGFsZXR0ZTogdMOqbiBj4bunYSBi4bqjbmcgbcOgdSwgY8OzIHRo4buDIGzDoCBzdHJpbmcgaG/hurdjIGluZGV4Lg0KDQpkaXJlY3Rpb246IHRo4bupIHThu7EgY8OhYyBtw6B1IHRyb25nIGLhuqNuZyBtw6B1LCAxIGzDoCB0aOG7qSB04buxIHTEg25nIGThuqduLCAtMSBsw6AgbmfGsOG7o2MgY2hp4buBdS4NCg0KdmFsdWVzOiDEkMOhbmggc+G7kSB0aOG7qSB04buxIHRyb25nIHZlY3RvciBncmFkaWVudCwgaOG6p3UgaOG6v3QgxJHhu4MgTlVMTA0KDQpzcGFjZTogS2jDtG5nIGdpYW4gbcOgdSBz4bqvYyDEkeG7gyB04burIMSRw7MgdMOtbmggdG/DoW4gZ3JhZGllbnQsIG7Dqm4gY2jhu41uIGzDoCDigJxMYWLigJ0gdsOsIG5o4buvbmcgc3BhY2Uga2jDoWMgxJHDoyBkZXByZWNpYXRlZC4NCg0KbmEudmFsdWU6IG3DoHUgc+G7rSBk4bulbmcgY2hvIG1pc3NpbmcgdmFsdWUuDQoNCmd1aWRlOiBLaeG7g3UgxJHDoW5oIG3DoHUgZ+G7k20g4oCcY29sb3VyYmFy4oCdIGNobyBjw6FjIG3DoHUgbGnDqm4gdOG7pWMsIOKAnGxlZ2VuZOKAnSBjaG8gY8OhYyBtw6B1IHBow6JuIGxv4bqhaS4NCg0KDQpgYGB7cn0NCiNtw6B1IHPhuq9jIG3hurdjIMSR4buLbmggdGhlbyBjbGFzcw0KKHAgPC0gZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArDQpnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBjbGFzcykpKQ0KYGBgDQoNCmBgYHtyfQ0KI3NjYWxlIHRoZW8gY29sb3IgYnJld2VyDQpwICsgc2NhbGVfY29sb3VyX2JyZXdlcigiQ2xhc3Mgb2Yg3LluIGNhcnMiKQ0KYGBgDQoNCmBgYHtyfQ0KI3NjYWxlIHPhu60gZOG7pW5nIHBhbGV0dGVzIEFjY2VudCBj4bunYSBRdWFsaXRhdGl2ZQ0KcCArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkFjY2VudCIpDQpgYGANCg0KYGBge3J9DQojc2NhbGUgc+G7rSBk4bulbmcgcGFsZXR0ZXMgU2V0MSBj4bunYSBzZXF1ZW50aWFsIA0KcCArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDEiKQ0KYGBgDQpgYGB7cn0NCiNTY2FsZSB0cm9uZyBiaeG7g3UgxJHhu5MgaGlzdG9ncmFtDQooaCA8LSBnZ3Bsb3QoZGlhbW9uZHMsIGFlcyh4PXByaWNlLCBmaWxsPWN1dCkpKw0KICAgIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uID0gImRvZGdlIiwgYmlud2lkdGggPSAxNTAwKSkNCmBgYA0KDQpgYGB7cn0NCiNMxrB1IMO9IMSR4buRaSB24bubaSBiaeG7g3UgxJHhu5MgYmFyY2hhcnQgdGEgcGjhuqNpIGTDuW5nIHNjYWxlX2ZpbGwgdGhheSBjaG8gc2NhbGVfY29sb3VyDQpoICsgc2NhbGVfZmlsbF9icmV3ZXIoKQ0KYGBgDQoNCmBgYHtyfQ0KI8SQ4bqjbyBuZ8aw4bujYyB24buLIHRyw60gbcOgdSBz4bqvYw0KaCArIHNjYWxlX2ZpbGxfYnJld2VyKGRpcmVjdGlvbiA9IC0xKQ0KYGBgDQoNCmBgYHtyfQ0KI1Phu60gZOG7pW5nIHNjYWxlX2ZpbGwgdHJvbmcgaGVhdG1hcA0KKHYgPC0gZ2dwbG90KGZhaXRoZnVsZCkgKyBnZW9tX3RpbGUoYWVzKHdhaXRpbmcsIGVydXB0aW9ucywgZmlsbA0KPSBkZW5zaXR5KSkpDQpgYGANCg0KYGBge3J9DQojU2NhbGUgdGhlbyBtw6B1IHPhuq9jIGLhurFuZyBuaMOzbSBtw6B1IHBow6JuIHTDoW4gU3BlY3RyYWwgY+G7p2EgcGFsYXR0ZSBEaXZlcmdpbmcuDQp2ICsgc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZSA9ICJTcGVjdHJhbCIpDQpgYGANCg0KYGBge3J9DQojU2NhbGUgdGhlbyBiaeG6v24gcGjDom4gbG/huqFpIGNsYXNzDQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsgDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGNsYXNzKSkNCmBgYA0KDQojIyBTY2FsZSB0aGVvIHRo4budaSBnaWFuDQpzY2FsZV8qX2RhdGU6IHNjYWxlIHRpbWUgY2hvIGPDoWMga2nhu4N1IHRo4budaSBnaWFuIHRodeG7mWMgY2xhc3MgRGF0ZQ0KDQpzY2FsZV8qX2RhdGV0aW1lOiBkw6BuaCBjaG8gY8OhYyBjbGFzcyBQT1NJWGN0DQoNCnNjYWxlXypfdGltZTogZMOgbmggY8OzIGPDoWMga2nhu4N1IHRo4budaSBnaWFuIHRodeG7mWMgY2xhc3MgaG1zDQoNCkPDuiBwaMOhcCBjaHVuZzoNCg0Kc2NhbGVfeV9kYXRldGltZShuYW1lID0gd2FpdmVyKCksIGJyZWFrcyA9IHdhaXZlcigpLA0KZGF0ZV9icmVha3MgPSB3YWl2ZXIoKSwgbGFiZWxzID0gd2FpdmVyKCksIGRhdGVfbGFiZWxzID0gd2FpdmVyKCksDQptaW5vcl9icmVha3MgPSB3YWl2ZXIoKSwgZGF0ZV9taW5vcl9icmVha3MgPSB3YWl2ZXIoKSwgdGltZXpvbmUgPSBOVUxMLA0KbGltaXRzID0gTlVMTCwgZXhwYW5kID0gd2FpdmVyKCksIHBvc2l0aW9uID0gImxlZnQiKQ0KDQpUcm9uZyDEkcOzOg0KDQpicmVhazogS2hv4bqjbmcgYnJlYWsgbMOgIGJhbyBuaGnDqnUsIGPDsyB0aOG7gyBuaOG6rW4gY8OhYyBnacOhIHRy4buLIE5VTEwgKGtow7RuZyBicmVhayksIHdhaXZlcigpIHRoZW8gbeG6t2MgxJHhu4tuaCBwYWNrYWdlIHTDrW5oIHRvw6FuLCBt4buZdCBudW1lcmljIHZlY3RvciBj4bunYSBwb3NpdGlvbiwgbeG7mXQgaMOgbSBz4buRIHRy4bqjIHbhu4EgY8OhYyBraG/huqNuZyBicmVha3MuDQoNCmRhdGVfYnJlYWs6IFjDoWMgxJHhu4tuaCBraG/huqNuZyB0aOG7nWkgZ2lhbiBnaeG7r2EgY8OhYyBixrDhu5tjIGxpw6puIHRp4bq/cCBsw6AgYmFvIG5oacOqdSBuaMawIOKAnDIgd2Vla3PigJ0sIOKAnDEwIHllYXJz4oCdLOKApg0KDQpsYWJlbHM6IEzDoCBt4buZdCB0cm9uZyBuaOG7r25nIGdpw6EgdHLhu4sgTlVMTCAoa2jDtG5nIGPDsyBsYWJlbHMpLCB3YWl2ZXIoKSBsw6AgZGVmYXVsdCBsYWJlbCBj4bunYSBwYWNrYWdlIHTDrW5oIHJhLCBt4buZdCBjaGFyYWN0ZXIgdmVjdG9yIGfDoW4gbmjDo24gY2hvIGxhYmVscyhwaOG6o2kgY8OzIMSR4buZIGTDoGkgPSBicmVha3MpLCBob+G6t2MgbeG7mXQgZnVuY3Rpb24gdHLhuqMgduG7gSBsYWJlbHMuDQoNCmRhdGVfbGFiZWxzOiBMw6Aga8OtIHThu7EgxJHhu4tuaCBk4bqhbmcgZm9ybWF0IHRo4budaSBnaWFuIGNobyBsYWJlbHMuIFZEIOKAnCVZICVi4oCdLCBjb2RlIG7DoHkgcGjhuqNpIHR1w6JuIHRoZW8ga2nhu4N1IHN0cmZ0aW1lLiBO4bq/dSBsYWJlbHMgxJHGsOG7o2MgxJHhu4tuaCBk4bqhbmcgY8O5bmcgduG7m2kgZGF0ZV9sYWJlbHMgdGjDrCBkYXRlX2xhYmVscyB0aOG6r25nLg0KDQpsaW1pdHM6IGzDoCB2ZWN0b3IgY3VuZyBj4bqlcCDEkeG7mSBkw6BpIGPhu6dhIHNjYWxlIGfhu5NtIDIgxJFp4buDbSDEkeG6p3UgdsOgIGN14buRaS4gQ2jhurNuZyBo4bqhbiBuaMawIGMoYXMuRGF0ZSjigJwyMDE4LTA0LTIx4oCdKS03LE5BKSwgYyhhcy5EYXRlKOKAnDIwMTgtMDQtMjHigJ0pLGFzLkRhdGUo4oCcMjAxOS0wNC0yMeKAnSkpDQoNCg0KDQoNCmBgYHtyfQ0KIyBDaOG7qWEgZ2nDoSB0cuG7iyBkYXRlIHRpbWUgMjkgbmfDoHkgdHLGsOG7m2MNCmxhc3RfbW9udGggPC0gU3lzLkRhdGUoKSAtIDA6MjkNCiNydW5pZiB04bqhbyByYSAzMCBnacOhIHRy4buLIMSR4buZIGzhu4djaCBjaHXhuqluIHJhbmRvbQ0KZGYgPC0gZGF0YS5mcmFtZSggZGF0ZSA9IGxhc3RfbW9udGgsIHByaWNlID0gcnVuaWYoMzApICkNCiNW4bq9IGJp4buDdSDEkeG7kyB24bubaSDEkeG7i25oIGThuqFuZyB0aW1lIGzDoCB5eXl5LW1tLWRkDQojc2NhbGUgdGltZSBjaG8gY8OhYyBraeG7g3UgdGjhu51pIGdpYW4gdGh14buZYyBjbGFzcyBEYXRlDQpnZ3Bsb3QoZGYsYWVzKGRhdGUscHJpY2UpKSsgZ2VvbV9saW5lKCkgKw0Kc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzID0gIiVZLSVtLSVkIikNCmBgYA0KDQpgYGB7cn0NCiNLaGFpIGLDoW8ga2hv4bqjbmcgdGjhu51pIGdpYW4gY+G7p2EgY8OhYyBixrDhu5tjIGxpw6puIHRp4bq/cCAoZGF0ZV9icmVha3MpIGzDoCAxIHR14bqnbg0KIyBkYXRlX2xhYmVscyBk4bqhbmcgZm9ybWF0IGzDoCB0deG6p24NCmdncGxvdChkZixhZXMoZGF0ZSxwcmljZSkpKw0KICBnZW9tX2xpbmUoKSArIA0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMSB3ZWVrIixkYXRlX2xhYmVscyA9ICIlVyIpDQpgYGANCg0KYGBge3J9DQojQ2h1eeG7g24gc2FuZyBkYXRlIGJyZWFrID0gMSBuZ8OgeQ0KI2RhdGVfbGFiZWxzIHRoZW8gZm9ybWF0IGzDoCBuZ8OgeQ0KZ2dwbG90KGRmLGFlcyhkYXRlLHByaWNlKSkrIGdlb21fbGluZSgpICsNCnNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIGRheSIsZGF0ZV9sYWJlbHMgPSAiJWQiKQ0KYGBgDQoNCmBgYHtyfQ0KI0RhdGVicmVhayA9IDEgbmfDoHksIGRhdGUgbGltaXQgbMOgIGLhuq90IMSR4bqndSB04burIDcgbmfDoHkgdHLGsOG7m2MgY2hvIHThu5tpIG5heQ0KZ2dwbG90KGRmLGFlcyhkYXRlLHByaWNlKSkrIGdlb21fbGluZSgpICtzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMSBkYXkiLGRhdGVfbGFiZWxzID0gIiVZLSViLQ0KJWQiLGxpbWl0cyA9IGMoU3lzLkRhdGUoKS03LE5BKSkNCmBgYA0KDQojIyBDaGlhIMSR4buTIHRo4buLIHRow6BuaCBuaGnhu4F1IGZhY2V0DQoNCjIgaMOgbSBmYWNldF93cmFwKCkgdsOgIGZhY2V0X2dyaWQoKSBz4butIGThu6VuZyDEkeG7gyBwaMOibiBjaGlhIDEgxJHhu5MgdGjhu4sgdGjDoG5oIG5oaeG7gXUgZmFjZXQga2jDoWMgbmhhdS4gVHJvbmcgxJHDszoNCg0KZmFjZXRfd3JhcCgpOiBwaMOibiBjaGlhIDEgbeG6o25oIMSR4buTIHRo4buLIHRow6BuaCBuaGnhu4F1IG3huqNuaCBmYWNldCBraMOhYyBuaGF1LiBOw7MgxJHGsOG7o2Mgc+G7rSBk4bulbmcgcGjhu5UgYmnhur9uIGjGoW4gc28gduG7m2kgZmFjZXRfZ3JpZCgpIHbDrCBjw6FjIGhp4buDdSB0aOG7iyDEkeG7gXUgxJHGsGEgcmEga+G6v3QgcXXhuqMgaMOsbmggY2jhu68gbmjhuq10LiBDw7ogcGjDoXA6DQoNCmZhY2V0X3dyYXAoZmFjZXRzLCBucm93ID0gTlVMTCwgbmNvbCA9IE5VTEwsIHNjYWxlcyA9ICJmaXhlZCIsDQogIHNocmluayA9IFRSVUUsIGxhYmVsbGVyID0gImxhYmVsX3ZhbHVlIiwgYXMudGFibGUgPSBUUlVFLA0KICBkcm9wID0gVFJVRSwgZGlyID0gImgiLCBzdHJpcC5wb3NpdGlvbiA9ICJ0b3AiKQ0KICANClRyb25nIMSRw7MgbeG7mXQgc+G7kSBhcmd1bWVudCBjaMOtbmg6DQoNCiogZmFjZXRzOiBsw6AgY8O0bmcgdGjhu6ljIGhv4bq3YyB2ZWN0b3IuIEtoaSBsw6AgY8O0bmcgdGjhu6ljIHRow6wgY2jhu4kgY8OzIDEgc2lkZSBjaOG6s25nIGjhuqFuIH5hK2IsIGtoaSBsw6AgdmVjdG9yIHPhur0gY8OzIGThuqFuZyBjKOKAnGHigJ0s4oCcYuKAnSkgcXVpIMSR4buLbmggY2hp4buBdSBz4butIGThu6VuZyDEkeG7gyBwaMOibiBsb+G6oWkgZOG7ryBsaeG7h3UgdGjDoG5oIGPDoWMgbmjDs20gdsOgIG3hu5dpIG5ow7NtIHPhur0gxJHGsOG7o2MgduG6vSB0csOqbiAxIGZhY2V0Lg0KDQoqIG5yb3csIG5jb2w6IHF1aSDEkeG7i25oIHPhu5EgZMOybmcsIHPhu5EgY+G7mXQgxJHhu4Mgc+G6r3AgeOG6v3AgY8OhYyBmYWNldHMuDQoNCmxhYmVsbGVyOiB4w6FjIMSR4buLbmggbGFiZWwgxJHGsOG7o2MgaGnhu4NuIHRo4buLIGNobyBt4buXaSBmYWNldC4gVGjDtG5nIHRoxrDhu51uZyBz4bq9IGTDuW5nIOKAnGxhYmVsX2JvdGjigJ0NCg0KKiBzY2FsZXM6IHNjYWxlcyDhu58gY8OhYyB0cuG7pWMgeCx5IMSRxrDhu6NjIGZpeGVkLCBmcmVlIGhv4bq3YyBmcmVlIDEgdHJvbmcgMiB0cuG7pWMgduG7m2kgZnJlZV94IGhv4bq3YyBmcmVlX3kNCg0KKiBzdHJpcC5wb3NpdGlvbjogdGhheSDEkeG7lWkgduG7iyB0csOtIGPDoWMgbGFiZWwuIEPDsyBjw6FjIG9wdGlvbiAo4oCcdG9w4oCdLOKAnGJvdHRvbeKAnSzigJxsZWZ04oCdLOKAnHJpZ2h04oCdKQ0KDQoqIGRpcjogdGhheSDEkeG7lWkgY2hp4buBdSBiaeG7g3UgZGnhu4VuIGPDoWMgZmFjZXRzIGzDoCBk4buNYyBoYXkgbmdhbmcgZOG7sWEgdHLDqm4gbOG7sWEgY2jhu41uIOKAnHbigJ0gaG/hurdjIOKAnGjigJ0uDQoNCmBgYHtyfQ0KI1ThuqFvIG3hu410IGZhY2V0IHbhu5tpIGPDoWMgbmjDs20gcGjDom4gbG/huqFpIGThu7FhIHRyw6puIGNsYXNzDQpnZ3Bsb3QobXBnKSArIGdlb21fcG9pbnQoYWVzKGh3eSwgZGlzcGwpKSArDQpmYWNldF93cmFwKGMoImNsYXNzIikpDQpgYGANCg0KYGBge3J9DQojWMOhYyDEkeG7i25oIGxhYmVsICBoaeG7g24gdGjhu4sgbeG7l2kgZmFjZXQNCmdncGxvdChtcGcpICsgZ2VvbV9wb2ludChhZXMoaHd5LCBkaXNwbCkpICsNCmZhY2V0X3dyYXAoYygiY2xhc3MiKSwgbGFiZWxsZXIgPSAibGFiZWxfYm90aCIpDQpgYGANCg0KYGBge3J9DQojVGhheSDEkeG7lWkgduG7iyB0csOtIGPhu6dhIGPDoWMgbGFiZWwgeHXhu5FuZyBkxrDhu5tpDQpnZ3Bsb3QobXBnKSArIGdlb21fcG9pbnQoYWVzKGh3eSwgZGlzcGwpKSArDQpmYWNldF93cmFwKGMoImNsYXNzIiksIGxhYmVsbGVyID0gImxhYmVsX2JvdGgiLA0Kc3RyaXAucG9zaXRpb24gPSAiYm90dG9tIikNCmBgYA0KDQpgYGB7cn0NCiNUaGF5IMSR4buVaSBjaGnhu4F1IGhp4buDbiB0aOG7iyBmYWNldHMgdOG7qyBuZ2FuZyBzYW5nIGThu41jIHPhu60gZOG7pW5nIGRpcj0idiINCmdncGxvdChtcGcpICsgDQogIGdlb21fcG9pbnQoYWVzKGh3eSwgZGlzcGwpKSArIA0KICBmYWNldF93cmFwKGMoImNsYXNzIiksIGRpciA9ICJ2Iiwgc3RyaXAucG9zaXRpb24gPSAiYm90dG9tIikNCmBgYA0KDQpgYGB7cn0NCiNDw6FjIGZhY2V0IMSRYW5nIGPDsyBzY2FsZSBmaXhlZChj4buRIMSR4buLbmgpLCBjaMO6bmcgdGEgbXXhu5FuIGNobyB0cuG7pWMgeSBmcmVlIHThu6ljIGzDoCBt4buXaSBt4buZdCBmYWNldCBz4bq9IGPDsyBt4buZdCBzY2FsZSByacOqbmcgDQpnZ3Bsb3QobXBnKSArIGdlb21fcG9pbnQoYWVzKGh3eSwgZGlzcGwpKSArDQpmYWNldF93cmFwKGMoImNsYXNzIiksIGRpciA9ICJ2Iiwgc3RyaXAucG9zaXRpb24gPSAiYm90dG9tIiwNCnNjYWxlcyA9ICJmcmVlX3kiKQ0KYGBgDQoNCmBgYHtyfQ0KI07hur91IG114buRbiBjw6FjIMSR4buTIHRo4buLIGzhurdwIGzhuqFpIGThu68gbGnhu4d1IHbDoCBjaOG7iSBoaWdodGxpZ2h0IGPDoWMgY2F0ZWdvcnkgdGh14buZYyBmYWNldCDEkcOzIHRow6wgcGjhuqNpIHThuqFvIG3hu5l0IGZhY2V0IGtow7RuZyBjaOG7qWEgZmFjZXQgdmFyaWFibGUNCiNz4butIGThu6VuZyBow6BtIHRyYW5zZm9ybSDEkeG7gyB04bqhbyBt4buZdCBmYWNldCBtw6AgY8OzIGNsYXNzPSBOdWxsIHThu6ljIGzDoCBraMO0bmcgY2jhu6lhIGPDoWMgY2F0ZWdvcnkgdGh14buZYyBmYWNldC4NCmdncGxvdChtcGcsYWVzKGRpc3BsLCBod3kpKSArIGdlb21fcG9pbnQoZGF0YSA9IHRyYW5zZm9ybShtcGcsIGNsYXNzDQo9IE5VTEwpLCBjb2xvdXIgPSAiZ3JleSIpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfd3JhcChjKCJjbGFzcyIpLA0KZGlyID0gInYiLCBzdHJpcC5wb3NpdGlvbiA9ICJib3R0b20iLCBzY2FsZXMgPSAiZnJlZV95IikNCmBgYA0KDQpgYGB7cn0NCiNC4bqjbiBjaOG6pXQgxJHhu5MgdGjhu4sgdHLDqm4gZ+G7k20gMiBs4bubcCwgbOG7m3AgxJHhuqd1IHRpw6puIGzDoCB0w7QgY2hvIHRvw6BuIGLhu5kgxJHhu5MgdGjhu4ttw6B1IGdyZXksIGLGsOG7m2MgdGjhu6kgMiBsw6Ag4bufIGdlb21fcG9pbnQoKSBiw6puIGTGsOG7m2ksIGNo4buJIG5o4buvbmcgxJFp4buDbSBjw7MgY2F0ZWdvcnkgdGh14buZYyBmYWNldCBt4bubaSDEkcaw4bujYyB0w7QgbcOgdSDEkcOoIGzDqm4gbOG7m3AgMS4gDQojVMO0IG3DoHUgdGhlbyBzY2FsZSB0aOG6rXQgc+G6t2Mgc+G7oQ0KIyBT4butIGThu6VuZyBzY2FsZSB0aGVvIGdyYWRpZW50IHbDoCB04bqhbyAxMCBtw6B1IHThu7EgxJHhu5luZyBi4bqxbmcgaMOgbSB0ZXJyYWluDQpnZ3Bsb3QobXBnLGFlcyhkaXNwbCwgaHd5KSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBod3kpKSArDQpmYWNldF93cmFwKGMoImNsYXNzIiksIHN0cmlwLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzID0gdGVycmFpbi5jb2xvcnMoMTAsYWxwaGEgPSAwLjQpKQ0KYGBgDQoNCmBgYHtyfQ0KI8SQaeG7gXUgY2jhu4luaCB0aMOqbSB0aGVtZSB0aGVvIG3DoHUgdOG7kWkgYuG6sW5nIHRoZW1lX2RhcmsoKSBjaG8gZOG7hSBuaMOsbg0KZ2dwbG90KG1wZyxhZXMoZGlzcGwsIGh3eSkpICsgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gaHd5KSkgKw0KdGhlbWVfZGFyaygpICsgZmFjZXRfd3JhcChjKCJjbGFzcyIpLCBzdHJpcC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3VycyA9IHRlcnJhaW4uY29sb3JzKDEwLGFscGhhID0gMC40KSkNCmBgYA0KDQojIyBUaGnhur90IGzhuq1wIGdp4bubaSBo4bqhbiBjaG8gU2NhbGUNCg0KbGltcygpOiB0aGnhur90IGzhuq1wIGdp4bubaSBo4bqhbiBjaG8gY8OhYyBjYXRlZ29yeQ0KeGxpbSgpOiBUaGnhur90IGzhuq1wIGdp4bubaSBo4bqhbiBjaG8gdHLhu6VjIHgNCnlsaW0oKTogVGhp4bq/dCBs4bqtcCBnaeG7m2kgaOG6oW4gY2hvIHRy4bulYyB5DQoNCg0KYGBge3J9DQojxJDhu5MgdGjhu4sgZ+G7kWMNCmdncGxvdChtcGcpICsNCiAgZ2VvbV9wb2ludChhZXMoaHd5LCBkaXNwbCkpDQpgYGANCg0KYGBge3J9DQojVGhp4bq/dCBs4bqtcCBnaeG7m2kgaOG6oW4gY2hvIHggdOG7qyAxNS0yMCBz4butIGThu6VuZyB4bGltKDE1LDIwKQ0KZ2dwbG90KG1wZykgKyBnZW9tX3BvaW50KGFlcyhod3ksIGRpc3BsKSkgKyB4bGltKDE1LDIwKQ0KYGBgDQoNCmBgYHtyfQ0KI1RoaeG6v3QgbOG6rXAgZ2nhu5tpIGjhuqFuIGNobyB5IHThu6sgMy03DQpnZ3Bsb3QobXBnKSArDQogIGdlb21fcG9pbnQoYWVzKGh3eSwgZGlzcGwpKSArIA0KICB5bGltKDMsNykNCmBgYA0KDQpgYGB7cn0NCiNUaGnhur90IGzhuq1wIGdp4bubaSBo4bqhbiBjaG8gY2xhc3MgdGh14buZYyBjw6FjIG5ow7NtIGMoImNvbXBhY3QiLCJtaWRzaXplIiwic3V2IiwibWluaXZhbiIpDQojIEdp4bubaSBo4bqhbiA0IGNsYXNzDQpnZ3Bsb3QobXBnKSArIGdlb21fcG9pbnQoYWVzKGh3eSwgZGlzcGwsIGNvbG91ciA9IGNsYXNzKSkgKw0KbGltcyhjb2xvdXIgPSBjKCJjb21wYWN0IiwibWlkc2l6ZSIsInN1diIsIm1pbml2YW4iKSkNCmBgYA0KDQojIDIpIEdJ4bubaSB0aGnhu4d1IGdncGxvdDItTGluZSBncmFwaA0KDQpgYGB7cn0NCiMgU8awzIkgZHXMo25nIGRhdGFzZXQgcHJlc3N1cmUNCmRmIDwtIHByZXNzdXJlDQpoZWFkKGRmKQ0KYGBgDQoNCmBgYHtyfQ0KIyBUYcyjbyBsaW5lIGdyYXBoIHPGsMyJIGR1zKNuZyBoYcyAbSBnZ3Bsb3QoKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KZ2dwbG90KGRmLCBhZXMoeD10ZW1wZXJhdHVyZSwgeT1wcmVzc3VyZSkpICsgZ2VvbV9saW5lKCkNCmBgYA0KDQpgYGB7cn0NCiMgZ2nhu5tpIGjhuqFuIHbDuW5nIGNobyB0cuG7pWMgdOG7jWEgxJHhu5kgc+G7rSBz4bulbmcgeGxpbSB2w6AgeWxpbQ0KIyDhu54gxJHDonkgZ2nhu5tpIGjhuqFuIHkgdHJvbmcga2hv4bqjbmcgdOG7qyAyMDAgY2hvIHThu5tpIHkgbWF4DQpnZ3Bsb3QoZGYsIGFlcyh4PXRlbXBlcmF0dXJlLCB5PXByZXNzdXJlKSkgKyBnZW9tX2xpbmUoKSArDQp5bGltKDIwMCwgbWF4KHByZXNzdXJlKSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBT4butIGThu6VuZyB4bGltIGdp4bubaSBo4bqhbiB4IHRyb25nIGtob+G6o25nIDEwMCB04bubaSA1MDANCmdncGxvdChkZiwgYWVzKHg9dGVtcGVyYXR1cmUsIHk9cHJlc3N1cmUpKSArIGdlb21fbGluZSgpICsNCnhsaW0oMTAwLCA1MDApDQpgYGANCg0KYGBge3J9DQojIFRow6ptIHBvaW50cyBu4bqxbSB0csOqbiBsaW5lIGdyYXBoIGLhurFuZyBnZW9tX3BvaW50KCkNCmdncGxvdChkZiwgYWVzKHg9dGVtcGVyYXR1cmUsIHk9cHJlc3N1cmUpKSArDQpnZW9tX2xpbmUoKSArIGdlb21fcG9pbnQoKQ0KYGBgDQoNCmBgYHtyfQ0KIyBW4bubaSBsb2cgeS1heGlzLCB5IMSRxrDhu6NjIGJp4buDdSBkaeG7hW4gdGhlbyBk4bqhbmcgeT1sb2cxMCh4KQ0KZ2dwbG90KGRmLCBhZXMoeD10ZW1wZXJhdHVyZSwgeT1wcmVzc3VyZSkpICsNCmdlb21fbGluZSgpICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfeV9sb2cxMCgpDQpgYGANCg0KIyMgVGHMo28gbGluZSBncmFwaCB2xqHMgWkgbXVsdGlwbGUgbGluZXMNCg0KYGBge3J9DQojIFRhzKNvIGRhdGEgZnJhbWVzDQpubW9udGhzID0gMjQNCiNU4bqhbyByYSAxIG3huqNuZyB0xINuZyBk4bqnbiBi4bqvdCDEkeG6p3UgdOG7qyB0aMOhbmcgMS8yMDE1IHbDoCB0xINuZyBk4bqnbiB0aGVvIHRow6FuZyB24bubaSBz4buRIGzGsOG7o25nIHBo4bqnbiB04burIGPhu6dhIG3huqNuZyBsw6AgMjQuDQp4ID0gc2VxKGFzLkRhdGUoIjIwMTUvMS8xIiksIGJ5ID0gIm1vbnRoIiwgbGVuZ3RoLm91dCA9IG5tb250aHMpDQojIHJub3JtKG1lYW49ICxubW9udGhzKSB04bqhbyByYSAxIHZlY3RvciBn4buTbSAyNCBnacOhIHRy4buLIHJhbmRvbSB24bubaSBnacOhIHRy4buLIHRydW5nIGLDrG5oIHTGsMahbmcg4bupbmcNCmRmMSA8LSBkYXRhLmZyYW1lKGRhdGVzID0geCxWYXJpYWJsZSA9IHJub3JtKG1lYW4gPSAwLjc1LG5tb250aHMpKQ0KZGYyIDwtIGRhdGEuZnJhbWUoZGF0ZXMgPSB4LFZhcmlhYmxlID0gcm5vcm0obWVhbiA9IC0wLjc1LG5tb250aHMpKQ0KZGYzIDwtIGRhdGEuZnJhbWUoZGF0ZXMgPSB4LFZhcmlhYmxlID0gcm5vcm0obWVhbiA9IDAuMyxubW9udGhzKSkNCiNIaeG7g24gdGjhu4sgZGF0YWZyYW0gMSwgZGYxIGPDsyBrw61jaCB0aMaw4bubYyAyNHgyDQpoZWFkKGRmMSkNCmBgYA0KYGBge3J9DQojZGYyIGPFqW5nIGPDsyBrw61jaCB0aMaw4bubYyAyNHgyLCB24bubaSBnacOhIHRy4buLIHJhbmRvbSB0cnVuZyBiw6xuaCA9LTAuNzUNCmhlYWQoZGYyKQ0KYGBgDQpgYGB7cn0NCiMjZGYzIGPFqW5nIGPDsyBrw61jaCB0aMaw4bubYyAyNHgyLCB24bubaSBnacOhIHRy4buLIHJhbmRvbSB0cnVuZyBiw6xuaCA9MC4zDQpoZWFkKGRmMykNCmBgYA0KDQpgYGB7cn0NCiMgVOG6oW8gYmnhu4N1IMSR4buTIMSRxrDhu51uZyB24bubaSBuaGnhu4F1IGTDsm5nIGPDsyBtw6B1IGtow6FjIG5oYXUgdMawxqFuZyDhu6luZyB24bubaSAzIGRhdGEgZnJhbSDEkcaw4bujYyB04bqhbyDhu58gdHLDqm4NCmxpYnJhcnkoZ2dwbG90MikNCnAgPC0gZ2dwbG90KCkgKyBnZW9tX2xpbmUoZGF0YSA9IGRmMSwgYWVzKHggPSBkYXRlcywgeSA9IFZhcmlhYmxlKSwNCmNvbG9yID0gImJsdWUiKSArIGdlb21fbGluZShkYXRhID0gZGYyLCBhZXMoeCA9IGRhdGVzLCB5ID0gVmFyaWFibGUpLA0KY29sb3IgPSAicmVkIikgKyBnZW9tX2xpbmUoZGF0YSA9IGRmMywgYWVzKHggPSBkYXRlcywgeSA9IFZhcmlhYmxlKSwNCmNvbG9yID0gImdyZWVuIikNCnByaW50KHApDQpgYGANCg0KYGBge3J9DQojQ8OgaSDEkeG6t3QgdsOgIHPhu60gZOG7pW5nIGRwbHlyDQppbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpDQpsaWJyYXJ5KGRwbHlyKQ0KYGBgDQoNCmBgYHtyfQ0KIyBU4bqhbyBjYXRlZ29yeSBjaG8gMyBkYXRhIGZyYW1lcyB2w6AgZ+G7mXAgY2jDum5nIGzhuqFpIHPhu60gZOG7pW5nIGRwbHlyDQojICU+JSBkw7luZyDEkeG7gyB0cnV54buBbiBkYXRhZnJhbWUgIHbDoG8gMSBow6BtLCBiaW5kX3Jvd3MgZMO5bmcgxJHhu4MgZ+G7mXAgY8OhaSBkYXRhZnJhbWUgduG7m2kgbmhhdSwgbXV0YXRlIGTDuW5nIMSR4buDIHThuqFvIHRow6ptIDEgY+G7mXQgdHJvbmcgbeG7l2kgZGF0YSBmcmFtZSB2w6Agc+G7rSBk4bulbmcgbmjhu69uZyBjaOG7ryBjw6FpIEEsQixDIMSR4buDIGdpw7pwIHBow6JuIGJp4buHdCBkZjEsZGYyLGRmMyANCmxpYnJhcnkoZHBseXIpDQpkZiA8LSBkZjEgJT4lIG11dGF0ZShjYXQgPSAiQSIpICU+JSBiaW5kX3Jvd3MoZGYyICU+JQ0KbXV0YXRlKGNhdCA9ICJCIikpICU+JSBiaW5kX3Jvd3MoZGYzICU+JSBtdXRhdGUoY2F0ID0NCiJDIikpDQpoZWFkKGRmKQ0KYGBgDQpgYGB7cn0NCiMgVOG6oW8gbXVsdGlwbGUgbGluZXMgZ3JhcGhzIG3hu5dpIG3DoHUgc+G6r2MgdMawxqFuZyDhu6luZyB24bubaSB0aHXhu5ljIHTDrW5oIGNhdCB24burYSDEkcaw4bujYyB04bqhbyDhu58gdHLDqm4sIDMgbcOgdSB0xrDGoW5nIOG7qW5nIHbhu5tpIG3hu5dpIGRhdGFmcmFtZQ0KZ2dwbG90KGRmLCBhZXMoeCA9IGRhdGVzLCB5ID0gVmFyaWFibGUsIGNvbG9yID0NCmNhdCkpICsgZ2VvbV9saW5lKCkNCmBgYA0KDQojIyBUaGF5IMSR4buVaSBow6xuaCBk4bqhbmcgY+G7p2EgbGluZSANCg0KYGBge3J9DQojVGhheSDEkeG7lWkgaMOsbmggZOG6oW5nIGPhu6dhIGxpbmUgduG7m2kgbcOgdSB4YW5oIHbDoCDEkeG7qXQgcXXDo25nIA0KZ2dwbG90KGRmMSwgYWVzKHggPSBkYXRlcywgeSA9IFZhcmlhYmxlKSkgKyAgZ2VvbV9saW5lKGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEsIGNvbG91cj0iYmx1ZSIpDQpgYGANCiMjIFRoYXkgxJHDtMyJaSBoacyAbmggZGHMo25nIGN1zIlhIHBvaW50cw0KDQpgYGB7cn0NCiNUaMOqbSBuaOG7r25nIMSRaeG7g20gbcOgdSBo4buTbmcgY8OzIGvDrWNoIHRoxrDhu5tjID00IHbDoCBow6xuaCBk4bqhbmcgMjINCmdncGxvdChkZjEsIGFlcyh4ID0gZGF0ZXMsIHkgPSBWYXJpYWJsZSkpICsgIA0KICBnZW9tX2xpbmUoKSArDQogIGdlb21fcG9pbnQoc2l6ZT00LCBzaGFwZT0yMiwgY29sb3VyPSJkYXJrcmVkIiwgZmlsbD0icGluayIpDQoNCmBgYA0KDQojIyBUYcyjbyBHcmFwaCB3aXRoIGEgU2hhZGVkIEFyZWENCg0KYGBge3J9DQojVOG6oW8gYsOzbmcgbMOqbiAxIHbDuW5nIHBo4bunIGLhurFuZyBnZW9tX2FyZWEoKQ0KZ2dwbG90KGRmMSwgYWVzKHggPSBkYXRlcywgeSA9IFZhcmlhYmxlKSkgKyAgDQogIGdlb21fbGluZSgpICsNCiAgZ2VvbV9hcmVhKCkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkZjEsIGFlcyh4ID0gZGF0ZXMsIHkgPSBWYXJpYWJsZSkpICsgZ2VvbV9saW5lKCkgKw0KIyBU4bqhbyA4MCUgdHJvbmcgc3Xhu5F0IHbhu5tpIHRoaeG6v3QgbOG6rXAgYWxwaGEgYuG6sW5nIDAuMg0KIyBWaeG7h2MgbsOgeSBnacO6cCBjaG8gY2jDum5nIHRhIHbhuqtuIHRo4bqleSBuaOG7r25nIMSRxrDhu51uZyBsxrDhu5tpIGhp4buDbiB0aOG7iyBwaMOtYSBzYXUgbOG7m3AgcGjhu6cgeGFuaA0KZ2VvbV9hcmVhKGNvbG91cj0iYmxhY2siLCBmaWxsPSJncmVlbiIsIGFscGhhPS4yKQ0KYGBgDQoNCmBgYHtyfQ0KIyBUaOG7rSB24bubaSBhbHBoYSA9IDAuOA0KIyDEkOG7mSB0cm9uZyBzdeG7kXQgY2jhu4kgduG7m2kgMjAlIG7Dqm4gbmjhu69uZyDEkeG7q25nIGvhursgbeG7nSBob8OgbiB0b8OgbiBi4buLIHbDuW5nIHBo4bunIGNoZSBraHXhuqV0DQpnZ3Bsb3QoZGYxLCBhZXMoeCA9IGRhdGVzLCB5ID0gVmFyaWFibGUpKSArIGdlb21fbGluZSgpICsNCmdlb21fYXJlYShjb2xvdXI9ImJsYWNrIiwgZmlsbD0iZ3JlZW4iLCBhbHBoYT0uOCkNCmBgYA0KDQojIyBU4bqhbyBuaOG7r25nIHbDuW5nIHBo4bunIHjhur9wIGNo4buTbmcgbMOqbiBuaGF1IA0KDQpgYGB7cn0NCiNIw6xuaCBoaeG7g24gdGjhu4sgY2hvIHRo4bqleSAzIGRhdGEgZnJhbWUgQSxCLEMgeOG6v3AgY2jhu5NuZyBsw6puIG5oYXUgYuG6sW5nIDMgbcOgdSBwaOG7pyBraMOhYyBuaGF1IA0KZ2dwbG90KGRmLCBhZXMoeCA9IGRhdGVzLCB5ID0gVmFyaWFibGUsIGZpbGwgPSBjYXQpKSArDQogIGdlb21fYXJlYSgpDQpgYGANCg0KYGBge3J9DQojIFRoaeG6v3QgbOG6rXAgxJHhu5kgdHJvbmcgc3Xhu5F0ID0wLjYgduG7m2kgYWxwaGE9MC40IHbhu5tpIGtodSB24buxYyBwaOG7pyB0aGVvIG3DoHUgdOG7kWkNCiNzY2FsZSB0aGVvIGLhuqNuZyBtw6B1IHBhbGxldGUgbcOgdSB4YW5oLCB24bubaSBraG/huqNuZyBjw6FjaCBnaeG7r2EgMiBixrDhu5tjIGxpw6puIHRp4bq/cCB0xrDGoW5nIOG7qW5nIHZp4buHYyBjaHV54buDbiDEkeG7lWkgZGF0YWZyYW1lIG7DoHkgc2FuZyBkYXRhZnJhbWUga2jDoWMgdGhlbyBsb+G6oWkgY2F0Lg0KZ2dwbG90KGRmLCBhZXMoeCA9IGRhdGVzLCB5ID0gVmFyaWFibGUsIGZpbGwgPSBjYXQpKSArDQpnZW9tX2FyZWEoY29sb3VyPSJibGFjayIsIHNpemU9LjIsIGFscGhhPS40KSArDQpzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJCbHVlcyIsIGJyZWFrcz1yZXYobGV2ZWxzKGNhdCkpKQ0KP3Jldg0KYGBgDQoNCiMjIFRow6ptIGNvbmZpZGVuY2UgcmVnaW9uDQoNCmBgYHtyfQ0KIyBUacyBbmggOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgY2hvIHZhcmlhYmxlDQojIFZpw6rMgXQgaGHMgG0gdGnMgW5oIENJDQpjb25maWRlbmNlX2ludGVydmFsIDwtIGZ1bmN0aW9uKHZlY3RvciwgaW50ZXJ2YWwpIHsNCiAgIyDEkOG7mSBs4buHY2ggY2h14bqpbiBj4bunYSBzYW1wbGUNCiAgdmVjX3NkIDwtIHNkKHZlY3RvcikNCiAgIyBTYW1wbGUgc2l6ZQ0KICBuIDwtIGxlbmd0aCh2ZWN0b3IpDQogICMgR2nDoSB0cuG7iyB0cnVuZyBiw6xuaCBj4bunYSBzYW1wbGUNCiAgdmVjX21lYW4gPC0gbWVhbih2ZWN0b3IpDQogICMgTOG7l2kgZOG7sWEgdsOgbyBz4buxIHBow6JuIGLhu5EgdCB0w61uaCBi4bqxbmcgY8OhY2ggc+G7rSBk4bulbmcgaMOgbSBxdA0KICBlcnJvciA8LSBxdCgoaW50ZXJ2YWwgKyAxKS8yLCBkZiA9IG4gLSAxKSAqIHZlY19zZCAvIHNxcnQobikNCiAgIyBDb25maWRlbmNlIGludGVydmFsIGFzIGEgdmVjdG9yDQogICMgcmVzdWx0IDwtIGRhdGEuZnJhbWUoImxvd2VyIiA9IHZlY19tZWFuIC0gZXJyb3IsICJ1cHBlciIgPSB2ZWNfbWVhbiArIGVycm9yKQ0KICAjIFR1eSBuaGnDqm4gxJHDqsyJIHRhzKNvIHJhIGhhaSBjw7TMo3QgbG93ZXIgdmHMgCB1cHBlciBjaG8gdmVjdG9yIHZhcmlhYmxlIHTDtGkgbGHMgG0gbmjGsCBzYXU6DQogIHJlc3VsdCA8LSBkYXRhLmZyYW1lKCJsb3dlciIgPSB2ZWN0b3IgLSBlcnJvciwgInVwcGVyIiA9IHZlY3RvciArIGVycm9yKQ0KICByZXR1cm4ocmVzdWx0KQ0KfQ0KI1bDrSBk4bulDQp2ZWN0b3IgPC0gYygxMiwgMTcsIDI0LCAzNSwgMjMsIDM0LCA1NikNCmNvbmZpZGVuY2VfaW50ZXJ2YWwodmVjdG9yLCAwLjkwKQ0KYGBgDQoNCmBgYHtyfQ0KIyBUw61uaCBjb25maWRlbmNlIHJlZ2lvbiB24bubaSB0aGFtIHPhu5EgdmVjdG9yIHRydXnhu4FuIHbDoG8gbMOgIHRodeG7mWMgVmFyaWFibGUgY+G7p2EgZGYxIA0KI0vhur90IGjhu6NwIGRmMSB24bubaSBjb25maWRlbmNlIHJlZ2lvbiB24burYSB0w61uaCB4b25nIHbDoCB0aMOgbmggMSBi4bqjbmcgZGYgbeG7m2kgduG7m2kgMiB0aHXhu5ljIHTDrW5oIG3hu5tpIGxvd2VyIHbDoCB1cHBlciDEkcaw4bujYyB0aMOqbSB2w6BvDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KcmFuZ2UgPC0gY29uZmlkZW5jZV9pbnRlcnZhbChkZjEkVmFyaWFibGUsIDAuOTUpDQpkZjEgPC0gY2JpbmQoZGYxLCByYW5nZSkNCmhlYWQoZGYxKQ0KYGBgDQoNCmBgYHtyfQ0KI1Phu60gZOG7pW5nIGdlb21fcmliYm9uKCkgxJHhu4MgbuG7kWkgbmjhu69uZyBnacOhIHRy4buLIGNobyB5bWluIGFuZCB5bWF4IHTGsMahbmcg4bupbmcgduG7m2kgY29uZmlkZW5jZSByZWdpb24gxJHGsOG7o2MgdMOtbmgg4bufIHRyw6puLCDEkeG7k25nIHRo4budaSDEkeG7gyB04bqhbyB2w7luZyBwaOG7pyB24bubaSB5bWF4IHltaW4gdMawxqFuZyDhu6luZy4NCg0KZ2dwbG90KGRmMSwgYWVzKHggPSBkYXRlcywgeT0gVmFyaWFibGUpKSArDQpnZW9tX3JpYmJvbihhZXMoeW1pbiA9IGxvd2VyLCB5bWF4ID0gdXBwZXIpLCBhbHBoYT0wLjIpICsNCmdlb21fbGluZSgpDQo/Z2VvbV9yaWJib24NCmBgYA0KDQpgYGB7cn0NCiMgU+G7rSBk4bulbmcgxJHGsOG7nW5nIGNo4bqlbSDEkeG7kW0gZOG7gyB04bqhbyByYSByYW5oIGdp4bubaSBjaG8gdXBwZXIgdsOgIGxvd2VyDQpnZ3Bsb3QoZGYxLCBhZXMoeD1kYXRlcywgeT1WYXJpYWJsZSkpICsNCiAgZ2VvbV9saW5lKGFlcyh5PWxvd2VyKSwgY29sb3VyPSJncmV5NTAiLCBsaW5ldHlwZT0iZG90dGVkIikgKw0KICBnZW9tX2xpbmUoYWVzKHk9dXBwZXIpLCBjb2xvdXI9ImdyZXk1MCIsIGxpbmV0eXBlPSJkb3R0ZWQiKSArDQogIGdlb21fbGluZSgpDQpgYGANCg0KIyMgROG7ryBsaeG7h3UgY2h14buXaSB0aOG7nWkgZ2lhbg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCiMgROG7ryBsaeG7h3UgZGVtbzogZWNvbm9taWNzIHRyb25nIGfDs2kgZ2dwbG90Mg0KaGVhZChlY29ub21pY3MpDQpgYGANCg0KIyMgVGHMo28gbGluZSBwbG90cyBjxqEgYmHMiW46DQoNCmBgYHtyfQ0KIyBCYXNpYyBsaW5lIHBsb3QgduG7m2kgaOG7hyBtw6B1IHhhbmggdsOgIMSR4buZIGTDoHkgPTINCmdncGxvdChkYXRhID0gZWNvbm9taWNzLCBhZXMoeCA9IGRhdGUsIHkgPSBwb3ApKSsNCiAgZ2VvbV9saW5lKGNvbG9yID0gIiMwMEFGQkIiLCBzaXplID0gMikNCmBgYA0KDQpgYGB7cn0NCiMgUGxvdCBt4buZdCB04bqtcCBjb24gY+G7p2EgZGF0YQ0KIyBU4bqtcCBjb24gbsOgeSBiYW8gZ+G7k20gbmjhu69uZyBkw7JuZyB0cm9uZyBlY29ub21pY3MgbcOgIGPDsyBkYXRlID4gMjAwNi0xLTEgDQpzcyA8LSBzdWJzZXQoZWNvbm9taWNzLCBkYXRlID4gYXMuRGF0ZSgiMjAwNi0xLTEiKSkNCmdncGxvdChkYXRhID0gc3MsIGFlcyh4ID0gZGF0ZSwgeSA9IHBvcCkpICsNCmdlb21fbGluZShjb2xvciA9ICIjRkM0RTA3Iiwgc2l6ZSA9IDIpDQpgYGANCg0KYGBge3J9DQojxJBp4buBdSBjaOG7iW5oIGvDrWNoIHRoxrDhu5tjIGxpbmUgduG7m2kgdmnhu4djIGNo4buJbmggc2l6ZT0gdGjGsMahbmcgMiB0aHXhu5ljIHTDrW5oDQpnZ3Bsb3QoZGF0YSA9IGVjb25vbWljcywgYWVzKHggPSBkYXRlLCB5ID0gcG9wKSkgKw0KZ2VvbV9saW5lKGFlcyhzaXplID0gdW5lbXBsb3kvcG9wKSwgY29sb3IgPSAiI0ZDNEUwNyIpDQpgYGANCg0KDQojIyBUYcyjbyBtdWx0aXBsZSB0aW1lIHNlcmllcyBwbG90cyANCg0KYGBge3J9DQojQ8OgaSDEkeG6t3QgdGlkeXIgdsOgIHPhu60gZOG7pW5nDQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5ciIpDQpsaWJyYXJ5KHRpZHlyKQ0KYGBgDQoNCmBgYHtyfQ0KIyDEkMOqzIkgdGHMo28gbXVsdGlwbGUgcGxvdCBixqHMiWkgMiBiacOqzIFuIHBzYXZlcnQgdmHMgCB1ZW1wbWVkIHRoZW8gZGF0ZXMuIMSQw6LMgHUgdGnDqm4gY8OizIBuIMSRacyjbmggaGnMgG5oIGxhzKNpIGRhdGEgc8awzIl1IGR1zKNuZyB0aWR5ciBwYWNrYWdlDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShkcGx5cikNCiMgY2jhu41uIHJhIHRodeG7mWMgdMOtbmggZGF0ZSxwc2F2ZXJ0LCB1ZW1wbWVkIHRyb25nIGVjb25vbWljcyB0xrDGoW5nIOG7qW5nIHbhu5tpIGtp4buDdSB0aHXhu5ljIHTDrW5oIHZhcmlhYmxlLCB2YWx1ZSwgZGF0ZQ0KZGYgPC0gZWNvbm9taWNzICU+JQ0KICBzZWxlY3QoZGF0ZSwgcHNhdmVydCwgdWVtcG1lZCkgJT4lDQogIGdhdGhlcihrZXkgPSAidmFyaWFibGUiLCB2YWx1ZSA9ICJ2YWx1ZSIsIC1kYXRlKQ0KaGVhZChkZiwgMykNCmBgYA0KDQpgYGB7cn0NCiMgTXVsdGlwbGUgbGluZSBwbG90DQojIFBsb3QgMiB0aHXhu5ljIHTDrW5oIHBzYXZlcnQgdsOgIHVlbXBtZWQgdGhlbyAyIG3DoHUga2jDoWMgbmhhdSB0deG7syBjaOG7jW4gdHJvbmcgdGhlbWUgdOG7kWkgdGhp4buDdSBraMO0bmcgY8OzIHRy4bulYyB0b+G6oSDEkeG7mQ0KZ2dwbG90KGRmLCBhZXMoeCA9IGRhdGUsIHkgPSB2YWx1ZSkpICsNCmdlb21fbGluZShhZXMoY29sb3IgPSB2YXJpYWJsZSksIHNpemUgPSAxKSArDQpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIikpICsNCnRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KYGBge3J9DQojIHBsb3Qgbmjhu69uZyB2w7luZyBwaOG7pw0KIyBQbG90IG5oaeG7gXUgdsO5bmcgY2jhu5NuZyBjaMOpbyBsw6puLCBkbyDEkcOieSBiaeG7g3UgxJHhu5MgZOG6oW5nIGhlYXRtYXAgbsOqbiB0YSBz4butIGThu6VuZyBzY2FsZV9maWxsX21hbnVhbCwgcGxvdCAyIHbDuW5nIGNo4buTbmcgY2jDqW8gdGhlbyAyIG3DoXUga2jDoWMgbmhhdQ0KZ2dwbG90KGRmLCBhZXMoeCA9IGRhdGUsIHkgPSB2YWx1ZSkpICsNCmdlb21fYXJlYShhZXMoY29sb3IgPSB2YXJpYWJsZSwgZmlsbCA9IHZhcmlhYmxlKSwNCmFscGhhID0gMC41LCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOCkpICsNCnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiKSkgKw0Kc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIikpDQoNCmBgYA0KDQogDQojIyBTZXQgZGF0ZSBheGlzIGxpbWl0cw0KDQpgYGB7cn0NCiMgQmFzZSBwbG90IHdpdGggZGF0ZSBheGlzDQojQmnhu4N1IMSR4buTIHh1IGjGsOG7m25nICB0aGVvIG5nw6B5DQpwIDwtIGdncGxvdChkYXRhID0gZWNvbm9taWNzLCBhZXMoeCA9IGRhdGUsIHkgPSBwc2F2ZXJ0KSkgKyANCiAgICAgZ2VvbV9saW5lKGNvbG9yID0gIiMwMEFGQkIiLCBzaXplID0gMSkNCnANCmBgYA0KDQpgYGB7cn0NCiMgU2V0IGF4aXMgbGltaXRzIGMobWluLCBtYXgpDQojVGhp4bq/dCBs4bqtcCBiaeG7g3UgxJHhu5MgY+G7mXQgeCB4deG6pXQgcGjDoXAgdOG7qyBuZ8OgeSBuaOG7jyBuaOG6pXQgbmjhuqV0IGzDoCAyMDAyLTEtMSB2w6AgbOG7m24gbmjhuqV0IGzDoCBjaG8gdOG7m2kgaGnhu4duIHThuqFpDQptaW4gPC0gYXMuRGF0ZSgiMjAwMi0xLTEiKQ0KbWF4IDwtIE5BDQpwKyBzY2FsZV94X2RhdGUobGltaXRzID0gYyhtaW4sIG1heCkpDQpgYGANCg0KIyMgxJBpzKNuaCBkYcyjbmd0IGRhdGUgYXhpcyBsYWJlbHMNCg0KYGBge3J9DQojIFRoaeG6v3QgbOG6rXAgdGhlbyDEkeG7i25oIGThuqFuZyB0aMOhbmcvbsSDbSBjaG8gdGh14buZYyB0w61uaCBkYXRldGltZQ0KcCArIHNjYWxlX3hfZGF0ZShkYXRlX2xhYmVscyA9ICIlYi8lWSIpDQpgYGANCg0KIyMgQWRkIHRyZW5kIHNtb290aGVkIGxpbmUNCg0KYGBge3J9DQojIFRow6ptIDEgZMOybmcgYmnhu4N1IGRp4buFbiB4dSBoxrDhu5tuZyBj4bunYSBiaeG7g3UgxJHhu5MgduG7m2kgcGjGsMahbmcgdGjhu6ljIGxvZXNzDQpwICsgc3RhdF9zbW9vdGgoDQogIGNvbG9yID0gIiNGQzRFMDciLCBmaWxsID0gIiNGQzRFMDciLA0KICBtZXRob2QgPSAibG9lc3MiDQogICkNCmBgYA0KDQojIyBnZ2ZvcnRpZnktIGdncG1pc2MNCg0KLSBnZ2ZvcnRpZnkgbMOgIHBhY2thZ2UgbeG7nyBy4buZbmcgY+G7p2EgZ2dwbG90Mg0KDQpnZ2ZvcnRpZnkgduG6vSBiaeG7g3UgxJHhu5MgY2h14buXaSB0aOG7nWkgZ2lhbiAocGxvdCB0aW1lIHNlcmllcyBvYmplY3RzKQ0Kem9vOjp6b29yZWcoKSwgeHRzOjp4dHMoKSwgdGltZVNlcmllczo6dGltU2VyaWVzKCksIHRzZXJpZXM6OmlydHMoKSwNCmZvcmVjYXN0Ojpmb3JlY2FzdCgpLCB2YXJzOnZhcnMoKS4NCg0KLSBnZ3BtaXNjIHBhY2thZ2U6IGN1bmcgY+G6pXAgMiBwaMawxqFuZyBwaMOhcCBjaG8gdGltZSBzZXJpZXMgb2JqZWN0Og0KDQpzdGF0X3BlYWtzKCkgZmluZHMgYXQgd2hpY2ggeCBwb3NpdGlvbnMgbG9jYWwgeSBtYXhpbWEgYXJlIGxvY2F0ZWQsYW5kDQoNCnN0YXRfdmFsbGV5cygpIGZpbmRzIGF0IHdoaWNoIHggcG9zaXRpb25zIGxvY2FsIHkgbWluaW1hIGFyZSBsb2NhdGVkLg0KDQpgYGB7cn0NCiNDw6BpIMSR4bq3dA0KaW5zdGFsbC5wYWNrYWdlcyggYygiZ2dmb3J0aWZ5IiwgImNoYW5nZXBvaW50IiwgInN0cnVjY2hhbmdlIiwNCiJnZ3BtaXNjIikgKQ0KYGBgDQoNCmBgYHtyfQ0KIyBMb2FkIHRoxrAgdmnhu4duDQpsaWJyYXJ5KGdnZm9ydGlmeSkNCmxpYnJhcnkobWFncml0dHIpICMgZm9yIHBpcGluZyAlPiUNCiMgU+G7rSBk4bulbmcgaMOgbSAgYXVwbG90IMSR4buDIGto4bqvYyBob+G6oSDEkeG7kWkgdMaw4bujbmcgY2h14buXaSB0aOG7nWkgZ2lhbiANCmF1dG9wbG90KEFpclBhc3NlbmdlcnMpDQpgYGANCg0KYGBge3J9DQojIFBow6F0IGhp4buHbiByYSBuaOG7r25nIMSRaeG7g20gdGhheSDEkeG7lWkgZOG7sWEgdHLDqm4gdHJ1bmcgYsOsbmggdsOgIHBoxrDGoW5nIHNhaSB0cm9uZyBkZiBBaXJQYXNzZW5nZXJzDQpBaXJQYXNzZW5nZXJzICU+JQ0KICBjaGFuZ2Vwb2ludDo6IGNwdC5tZWFudmFyKCkgJT4lICAjIElkZW50aWZ5IGNoYW5nZSBwb2ludHMNCiAgYXV0b3Bsb3QoKQ0KYGBgDQoNCmBgYHtyfQ0KIyBQaMOhdCBoaeG7h24gcmEgbmjhu69uZyBixrDhu5tjIG5o4bqjeSB0cm9uZyBkYXRhDQpzdHJ1Y2NoYW5nZTo6YnJlYWtwb2ludHMoTmlsZSB+IDEpICU+JSBhdXRvcGxvdCgpDQpgYGANCg0KYGBge3J9DQojIFBow6F0IGhp4buHbiBuaOG7r25nIMSRaeG7g20gY2FvIG5o4bqldCB0aGVvIHkocGVha3MpIHbDoCB0aOG6pXAgbmjhuqV0IHRoZW8geSh2YWxsZXlzKQ0KI05o4buvbmcgxJFp4buDbSBjYW8gbmjhuqV0IHTGsMahbmcg4bupbmcgbcOgdSDEkeG7jyB2w6AgdGjhuqVwIG5o4bqldCB0xrDGoW5nIOG7qW5nIG3DoHUgeGFuaA0KI0Phu5l0IHggdGhlbyB0aHXhu5ljIHTDrW5oIGRhdGV0aW1lIGzDoCBuxINtLCB2w6AgeSB0aGVvIHRodeG7mWMgdMOtbmggbHlueCwgbmjhu69uZyDEkWnhu4NtIHRo4bqlcCBuaOG6pXQgc+G6vSB04bqhbyB0aMOgbmggMSBnw7NjIDQ1IMSR4buZDQpsaWJyYXJ5KGdncG1pc2MpDQpnZ3Bsb3QobHlueCwgYXMubnVtZXJpYyA9IEZBTFNFKSArIGdlb21fbGluZSgpICsNCnN0YXRfcGVha3MoY29sb3VyID0gInJlZCIpICsNCnN0YXRfcGVha3MoZ2VvbSA9ICJ0ZXh0IiwgY29sb3VyID0gInJlZCIsIHZqdXN0ID0gLTAuNSwNCngubGFiZWwuZm10ID0gIiVZIikgKw0Kc3RhdF92YWxsZXlzKGNvbG91ciA9ICJibHVlIikgKw0Kc3RhdF92YWxsZXlzKGdlb20gPSAidGV4dCIsIGNvbG91ciA9ICJibHVlIiwgYW5nbGUgPSA0NSwNCnZqdXN0ID0gMS41LCBoanVzdCA9IDEsIHgubGFiZWwuZm10ID0gIiVZIikrDQp5bGltKC01MDAsIDczMDApDQpgYGANCg0K