Danh sách (list) trong
ngôn ngữ R
Trong ngôn ngữ R, danh sách (list) là một cấu trúc dữ liệu linh hoạt
có thể chứa các phần tử thuộc nhiều loại dữ liệu khác nhau, bao gồm con
số, chuỗi kí tự, vector, hàm và thậm chí là các danh sách khác (nested
list) hoặc một cách tổng quát: bất kì R object nào. Dưới đây là một phân
tích tổng quan về bản chất và đặc điểm của danh sách trong R:
Tính đa dạng: Danh sách có thể chứa đồng thời nhiều
loại dữ liệu. Ví dụ, một danh sách có thể lưu đồng thời giá trị số,
vector ký tự, một matrix, một dataframe, hàm và thậm chí là một danh
sách khác.
Đa số kết quả phân tích thống kê thường xuất ra dưới dạng list, bên
trong chứa nhiều thông tin. Ví dụ một mô hình có thể bao gồm bảng kết
quả, dữ liệu gốc, các thông số kỹ thuật. Dataframe trong R cũng có bản
chất là list.
Tính Đệ quy: Danh sách có thể chứa các danh sách
khác. Điều này làm cho chúng trở nên đệ quy, vì cấu trúc của chúng có
thể được lồng ghép vào nhau.
Kích thước Kích thước của danh sách đề cập đến số
lượng phần tử mà nó chứa, R list có thể có bất kỳ kích thước nào, kể cả
list rỗng (kích thước = 0)
Đặt tên: Các phần tử của danh sách có thể đặt tên.
Những tên này có thể được sử dụng để tham chiếu đến các phần tử, tính
năng này gần giống với key trong Python dictionary.
Chỉ mục: Các phần tử trong một danh sách có thể được
truy cập sử dụng ký hiệu ngoặc vuông kép [[ ]] hoặc dollar $ nếu các
phần tử được đặt tên. Ngoặc vuông đơn [ ] trả về một danh sách con thay
vì chính phần tử đó.
Linh hoạt: Kích thước và nội dung của danh sách có
thể được sửa đổi. Bạn có thể thêm, xóa và sửa đổi các phần tử bên trong
danh sách.
Tính linh động: Trong R, loại (type) của các phần tử
trong danh sách có thể thay đổi trong thời gian thi hành (giống như ngôn
ngữ Python hay JavaScript, và khác với ngôn ngữ C hay Java)
Python dictionary là cấu trúc dữ liệu tương đồng nhất về tính năng so
với R list, tuy nhiên Python dict có tốc độ tìm và truy xuất dữ liệu
nhanh hơn do dựa trên hashtable trong khi R list có hiệu suất kém hơn do
có bản chất là array.
Ví dụ về công dụng của
list trong lập trình hàm
Giả sử ta cần viết một hàm cho phép vẽ 1 trong 3 loại biểu đồ:
density plot, histogram hoặc dot plot, do người dùng tùy chọn qua
parameter plot_type:
Giải pháp sơ cấp thường được nghĩ đến là dùng câu lệnh điều kiện
(control flow) với if và else if như sau:
library(tidyverse)
dist_plot_1 <- function(data, x, plot_type) {
# Check if the provided column name is valid
if (!x %in% names(data)) {
stop("The specified column does not exist in the data frame.")
}
# Check if the plot type is valid
valid_plot_types <- c("kde", "hist", "dot")
if (!plot_type %in% valid_plot_types) {
stop("Unknown plot type. Valid plot types are: ", paste(valid_plot_types, collapse = ", "))
}
# Generate the plot based on the type
p <- ggplot(data, aes_string(x = x))
if (plot_type == "kde") {
p <- p + geom_density(fill = "blue", alpha = 0.5) +
labs(title = paste("Kernel Density Estimate of", x))
} else if (plot_type == "hist") {
p <- p + geom_histogram(fill = "red", alpha = 0.5) +
labs(title = paste("Histogram of", x))
} else if (plot_type == "dot") {
p <- p + geom_dotplot(binaxis = 'x', dotsize = 1) +
labs(title = paste("Dot Plot of", x))
}
# Return the plot
return(p)
}
Ta có thể áp dụng list để thay cho cấu trúc if_else, vì biết rằng
list có thể chứa bất cứ object nào, nên ở đây ta dùng list để chứa các
ggplot layers khác nhau (bản chất là hàm), với key là plot_type.
dist_plot_2a <- function(data, x, plot_type) {
# Check if the provided column name is valid
if (!x %in% names(data)) {
stop("The specified column does not exist in the data frame.")
}
# Define a list of ggplot layers for each plot type
plot_layers <- list(
kde = geom_density(aes_string(x = x), fill = "blue", alpha = 0.5),
hist = geom_histogram(aes_string(x = x), fill = "red", alpha = 0.5),
dot = geom_dotplot(aes_string(x = x), binaxis = 'x', dotsize = 1)
)
# Get the ggplot layer from the list based on plot_type
layer <- plot_layers[[plot_type]]
# Check if plot_type is valid and layer exists
if (is.null(layer)) {
stop("Unknown plot type. Valid plot types are: 'kde', 'hist', 'dot'.")
}
# Create the ggplot object with the specified layer and return it
p <- ggplot(data) + layer + labs(title = paste(plot_type, "of", x))
return(p)
}
Ta còn có thể dùng nested_list để truy xuất cả layer (function) và
title (string) cho mỗi plot_type
dist_plot_2b <- function(data, x, plot_type) {
# Validate input
if (!x %in% names(data)) {
stop("The specified column does not exist in the data frame.")
}
# Mapping of plot types to ggplot layers and additional settings
plot_layers <- list(
kde = list(layer = geom_density(fill = "blue", alpha = 0.5), title = "Kernel Density Estimate"),
hist = list(layer = geom_histogram(fill = "red", alpha = 0.5), title = "Histogram"),
dot = list(layer = geom_dotplot(binaxis = 'x', dotsize = 0.5), title = "Dot Plot")
)
# Retrieve the plot settings for the specified plot_type
plot_setting <- plot_layers[[plot_type]]
if (is.null(plot_setting)) {
stop("Unknown plot type. Valid plot types are: 'kde', 'hist', 'dot'.")
}
# Create the ggplot object and add the layer and title
p <- ggplot(data, aes_string(x = x)) +
plot_setting$layer +
labs(title = paste(plot_setting$title, "of", x))
return(p)
}
Một cách tổng quát, ta có thể dùng list để chứa các hàm anonymous với
nội dung tùy thích như sau:
dist_plot_3 <- function(data, x, plot_type) {
# Check if the provided column name is valid
if (!x %in% names(data)) {
stop("The specified column does not exist in the data frame.")
}
# Define a list of plot types and their corresponding ggplot2 functions
plot_types <- list(
"kde" = function() ggplot(data, aes_string(x = x)) +
geom_density(fill = "blue", alpha = 0.5),
"hist" = function() ggplot(data, aes_string(x = x)) +
geom_histogram(fill = "red", alpha = 0.5),
"dot" = function() ggplot(data, aes_string(x = x)) +
geom_dotplot(binaxis = 'x', dotsize = 1)
)
# Check if the plot type is valid and get the corresponding function
plot_func <- plot_types[[plot_type]]
if (is.null(plot_func)) {
stop("Unknown plot type. Valid plot types are: ", paste(names(plot_types), collapse = ", "))
}
# Generate and return the plot
p <- plot_func()
p + labs(title = paste(plot_type, "Plot of", x))
}
Thay vì dùng trực tiếp list, trong ngôn ngữ R còn có cấu trúc điều
kiện rẽ nhánh (switch), bản chất của nó cũng là một list chứa các object
được tạo ra trong lúc thi hành tùy theo từ khóa điều kiện
dist_plot_4 <- function(data, x, plot_type) {
# Check if the provided column name is valid
if (!x %in% names(data)) {
stop("The specified column does not exist in the data frame.")
}
# Define the plot
p <- ggplot(data, aes_string(x = x))
# Generate the plot based on the type using switch
p <- switch(plot_type,
kde = p + geom_density(fill = "blue", alpha = 0.5) +
labs(title = paste("Kernel Density Estimate of", x)),
hist = p + geom_histogram(fill = "red", alpha = 0.5) +
labs(title = paste("Histogram of", x)),
dot = p + geom_dotplot(binaxis = 'x',
dotsize = 1) +
labs(title = paste("Dot Plot of", x)),
stop("Unknown plot type. Valid plot types are: kde, hist, dot")
)
# Return the plot
return(p)
}
LS0tDQp0aXRsZTogIkRhbmggc8OhY2ggKFIgbGlzdCkgdsOgIOG7qW5nIGThu6VuZyINCmF1dGhvcjogIkzDqiBOZ+G7jWMgS2jhuqMgTmhpIg0KZGF0ZTogIjA1IFRow6FuZyAxMSBuxINtIDIwMjMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogZGVmYXVsdA0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBkZXY6IHN2Zw0KLS0tDQoNCmBgYHtyIHNldHVwLGluY2x1ZGU9Rn0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVCkNCmBgYA0KDQojIERhbmggc8OhY2ggKGxpc3QpIHRyb25nIG5nw7RuIG5n4buvIFINCg0KVHJvbmcgbmfDtG4gbmfhu68gUiwgZGFuaCBzw6FjaCAobGlzdCkgbMOgIG3hu5l0IGPhuqV1IHRyw7pjIGThu68gbGnhu4d1IGxpbmggaG/huqF0IGPDsyB0aOG7gyBjaOG7qWEgY8OhYyBwaOG6p24gdOG7rSB0aHXhu5ljIG5oaeG7gXUgbG/huqFpIGThu68gbGnhu4d1IGtow6FjIG5oYXUsIGJhbyBn4buTbSBjb24gc+G7kSwgY2h14buXaSBrw60gdOG7sSwgdmVjdG9yLCBow6BtIHbDoCB0aOG6rW0gY2jDrSBsw6AgY8OhYyBkYW5oIHPDoWNoIGtow6FjIChuZXN0ZWQgbGlzdCkgaG/hurdjIG3hu5l0IGPDoWNoIHThu5VuZyBxdcOhdDogYuG6pXQga8OsIFIgb2JqZWN0IG7DoG8uIETGsOG7m2kgxJHDonkgbMOgIG3hu5l0IHBow6JuIHTDrWNoIHThu5VuZyBxdWFuIHbhu4EgYuG6o24gY2jhuqV0IHbDoCDEkeG6t2MgxJFp4buDbSBj4bunYSBkYW5oIHPDoWNoIHRyb25nIFI6DQoNCioqVMOtbmggxJFhIGThuqFuZzoqKiBEYW5oIHPDoWNoIGPDsyB0aOG7gyBjaOG7qWEgxJHhu5NuZyB0aOG7nWkgbmhp4buBdSBsb+G6oWkgZOG7ryBsaeG7h3UuIFbDrSBk4bulLCBt4buZdCBkYW5oIHPDoWNoIGPDsyB0aOG7gyBsxrB1IMSR4buTbmcgdGjhu51pIGdpw6EgdHLhu4sgc+G7kSwgdmVjdG9yIGvDvSB04buxLCBt4buZdCBtYXRyaXgsIG3hu5l0IGRhdGFmcmFtZSwgaMOgbSB2w6AgdGjhuq1tIGNow60gbMOgIG3hu5l0IGRhbmggc8OhY2gga2jDoWMuDQoNCsSQYSBz4buRIGvhur90IHF14bqjIHBow6JuIHTDrWNoIHRo4buRbmcga8OqIHRoxrDhu51uZyB4deG6pXQgcmEgZMaw4bubaSBk4bqhbmcgbGlzdCwgYsOqbiB0cm9uZyBjaOG7qWEgbmhp4buBdSB0aMO0bmcgdGluLiBWw60gZOG7pSBt4buZdCBtw7QgaMOsbmggY8OzIHRo4buDIGJhbyBn4buTbSBi4bqjbmcga+G6v3QgcXXhuqMsIGThu68gbGnhu4d1IGfhu5FjLCBjw6FjIHRow7RuZyBz4buRIGvhu7kgdGh14bqtdC4gRGF0YWZyYW1lIHRyb25nIFIgY8WpbmcgY8OzIGLhuqNuIGNo4bqldCBsw6AgbGlzdC4NCg0KKipUw61uaCDEkOG7hyBxdXkqKjogRGFuaCBzw6FjaCBjw7MgdGjhu4MgY2jhu6lhIGPDoWMgZGFuaCBzw6FjaCBraMOhYy4gxJBp4buBdSBuw6B5IGzDoG0gY2hvIGNow7puZyB0cuG7nyBuw6puIMSR4buHIHF1eSwgdsOsIGPhuqV1IHRyw7pjIGPhu6dhIGNow7puZyBjw7MgdGjhu4MgxJHGsOG7o2MgbOG7k25nIGdow6lwIHbDoG8gbmhhdS4NCg0KKipLw61jaCB0aMaw4bubYyoqIEvDrWNoIHRoxrDhu5tjIGPhu6dhIGRhbmggc8OhY2ggxJHhu4EgY+G6rXAgxJHhur9uIHPhu5EgbMaw4bujbmcgcGjhuqduIHThu60gbcOgIG7DsyBjaOG7qWEsIFIgbGlzdCBjw7MgdGjhu4MgY8OzIGLhuqV0IGvhu7Mga8OtY2ggdGjGsOG7m2MgbsOgbywga+G7gyBj4bqjIGxpc3QgcuG7l25nIChrw61jaCB0aMaw4bubYyA9IDApDQoNCioqxJDhurd0IHTDqm46KiogQ8OhYyBwaOG6p24gdOG7rSBj4bunYSBkYW5oIHPDoWNoIGPDsyB0aOG7gyDEkeG6t3QgdMOqbi4gTmjhu69uZyB0w6puIG7DoHkgY8OzIHRo4buDIMSRxrDhu6NjIHPhu60gZOG7pW5nIMSR4buDIHRoYW0gY2hp4bq/dSDEkeG6v24gY8OhYyBwaOG6p24gdOG7rSwgdMOtbmggbsSDbmcgbsOgeSBn4bqnbiBnaeG7kW5nIHbhu5tpIGtleSB0cm9uZyBQeXRob24gZGljdGlvbmFyeS4NCg0KKipDaOG7iSBt4bulYzoqKiBDw6FjIHBo4bqnbiB04butIHRyb25nIG3hu5l0IGRhbmggc8OhY2ggY8OzIHRo4buDIMSRxrDhu6NjIHRydXkgY+G6rXAgc+G7rSBk4bulbmcga8O9IGhp4buHdSBuZ2/hurdjIHZ1w7RuZyBrw6lwIFtbIF1dIGhv4bq3YyBkb2xsYXIgJCBu4bq/dSBjw6FjIHBo4bqnbiB04butIMSRxrDhu6NjIMSR4bq3dCB0w6puLiBOZ2/hurdjIHZ1w7RuZyDEkcahbiBbIF0gdHLhuqMgduG7gSBt4buZdCBkYW5oIHPDoWNoIGNvbiB0aGF5IHbDrCBjaMOtbmggcGjhuqduIHThu60gxJHDsy4NCg0KKipMaW5oIGhv4bqhdDoqKiBLw61jaCB0aMaw4bubYyB2w6AgbuG7mWkgZHVuZyBj4bunYSBkYW5oIHPDoWNoIGPDsyB0aOG7gyDEkcaw4bujYyBz4butYSDEkeG7lWkuIELhuqFuIGPDsyB0aOG7gyB0aMOqbSwgeMOzYSB2w6Agc+G7rWEgxJHhu5VpIGPDoWMgcGjhuqduIHThu60gYsOqbiB0cm9uZyBkYW5oIHPDoWNoLg0KDQoqKlTDrW5oIGxpbmggxJHhu5luZyoqOiBUcm9uZyBSLCBsb+G6oWkgKHR5cGUpIGPhu6dhIGPDoWMgcGjhuqduIHThu60gdHJvbmcgZGFuaCBzw6FjaCBjw7MgdGjhu4MgdGhheSDEkeG7lWkgdHJvbmcgdGjhu51pIGdpYW4gdGhpIGjDoG5oIChnaeG7kW5nIG5oxrAgbmfDtG4gbmfhu68gUHl0aG9uIGhheSBKYXZhU2NyaXB0LCB2w6Aga2jDoWMgduG7m2kgbmfDtG4gbmfhu68gQyBoYXkgSmF2YSkNCg0KUHl0aG9uIGRpY3Rpb25hcnkgbMOgIGPhuqV1IHRyw7pjIGThu68gbGnhu4d1IHTGsMahbmcgxJHhu5NuZyBuaOG6pXQgduG7gSB0w61uaCBuxINuZyBzbyB24bubaSBSIGxpc3QsIHR1eSBuaGnDqm4gUHl0aG9uIGRpY3QgY8OzIHThu5FjIMSR4buZIHTDrG0gdsOgIHRydXkgeHXhuqV0IGThu68gbGnhu4d1IG5oYW5oIGjGoW4gZG8gZOG7sWEgdHLDqm4gaGFzaHRhYmxlIHRyb25nIGtoaSBSIGxpc3QgY8OzIGhp4buHdSBzdeG6pXQga8OpbSBoxqFuIGRvIGPDsyBi4bqjbiBjaOG6pXQgbMOgIGFycmF5Lg0KDQojIFbDrSBk4bulIHbhu4EgY8O0bmcgZOG7pW5nIGPhu6dhIGxpc3QgdHJvbmcgbOG6rXAgdHLDrG5oIGjDoG0NCg0KR2nhuqMgc+G7rSB0YSBj4bqnbiB2aeG6v3QgbeG7mXQgaMOgbSBjaG8gcGjDqXAgduG6vSAxIHRyb25nIDMgbG/huqFpIGJp4buDdSDEkeG7kzogZGVuc2l0eSBwbG90LCBoaXN0b2dyYW0gaG/hurdjIGRvdCBwbG90LCBkbyBuZ8aw4budaSBkw7luZyB0w7l5IGNo4buNbiBxdWEgcGFyYW1ldGVyIHBsb3RfdHlwZToNCg0KR2nhuqNpIHBow6FwIHPGoSBj4bqlcCB0aMaw4budbmcgxJHGsOG7o2MgbmdoxKkgxJHhur9uIGzDoCBkw7luZyBjw6J1IGzhu4duaCDEkWnhu4F1IGtp4buHbiAoY29udHJvbCBmbG93KSB24bubaSBpZiB2w6AgZWxzZSBpZiBuaMawIHNhdToNCg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNCg0KYGBge3J9DQpkaXN0X3Bsb3RfMSA8LSBmdW5jdGlvbihkYXRhLCB4LCBwbG90X3R5cGUpIHsNCiAgIyBDaGVjayBpZiB0aGUgcHJvdmlkZWQgY29sdW1uIG5hbWUgaXMgdmFsaWQNCiAgaWYgKCF4ICVpbiUgbmFtZXMoZGF0YSkpIHsNCiAgICBzdG9wKCJUaGUgc3BlY2lmaWVkIGNvbHVtbiBkb2VzIG5vdCBleGlzdCBpbiB0aGUgZGF0YSBmcmFtZS4iKQ0KICB9DQogIA0KICAjIENoZWNrIGlmIHRoZSBwbG90IHR5cGUgaXMgdmFsaWQNCiAgdmFsaWRfcGxvdF90eXBlcyA8LSBjKCJrZGUiLCAiaGlzdCIsICJkb3QiKQ0KICBpZiAoIXBsb3RfdHlwZSAlaW4lIHZhbGlkX3Bsb3RfdHlwZXMpIHsNCiAgICBzdG9wKCJVbmtub3duIHBsb3QgdHlwZS4gVmFsaWQgcGxvdCB0eXBlcyBhcmU6ICIsIHBhc3RlKHZhbGlkX3Bsb3RfdHlwZXMsIGNvbGxhcHNlID0gIiwgIikpDQogIH0NCiAgDQogICMgR2VuZXJhdGUgdGhlIHBsb3QgYmFzZWQgb24gdGhlIHR5cGUNCiAgcCA8LSBnZ3Bsb3QoZGF0YSwgYWVzX3N0cmluZyh4ID0geCkpDQogIA0KICBpZiAocGxvdF90eXBlID09ICJrZGUiKSB7DQogICAgcCA8LSBwICsgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArIA0KICAgICAgbGFicyh0aXRsZSA9IHBhc3RlKCJLZXJuZWwgRGVuc2l0eSBFc3RpbWF0ZSBvZiIsIHgpKQ0KICB9IGVsc2UgaWYgKHBsb3RfdHlwZSA9PSAiaGlzdCIpIHsNCiAgICBwIDwtIHAgKyBnZW9tX2hpc3RvZ3JhbShmaWxsID0gInJlZCIsIGFscGhhID0gMC41KSArIA0KICAgICAgbGFicyh0aXRsZSA9IHBhc3RlKCJIaXN0b2dyYW0gb2YiLCB4KSkNCiAgfSBlbHNlIGlmIChwbG90X3R5cGUgPT0gImRvdCIpIHsNCiAgICBwIDwtIHAgKyBnZW9tX2RvdHBsb3QoYmluYXhpcyA9ICd4JywgZG90c2l6ZSA9IDEpICsgDQogICAgICBsYWJzKHRpdGxlID0gcGFzdGUoIkRvdCBQbG90IG9mIiwgeCkpDQogIH0NCiAgDQogICMgUmV0dXJuIHRoZSBwbG90DQogIHJldHVybihwKQ0KfQ0KDQpgYGANCg0KVGEgY8OzIHRo4buDIMOhcCBk4bulbmcgbGlzdCDEkeG7gyB0aGF5IGNobyBj4bqldSB0csO6YyBpZl9lbHNlLCB2w6wgYmnhur90IHLhurFuZyBsaXN0IGPDsyB0aOG7gyBjaOG7qWEgYuG6pXQgY+G7qSBvYmplY3QgbsOgbywgbsOqbiDhu58gxJHDonkgdGEgZMO5bmcgbGlzdCDEkeG7gyBjaOG7qWEgY8OhYyBnZ3Bsb3QgbGF5ZXJzIGtow6FjIG5oYXUgKGLhuqNuIGNo4bqldCBsw6AgaMOgbSksIHbhu5tpIGtleSBsw6AgcGxvdF90eXBlLg0KDQpgYGB7cn0NCmRpc3RfcGxvdF8yYSA8LSBmdW5jdGlvbihkYXRhLCB4LCBwbG90X3R5cGUpIHsNCiAgIyBDaGVjayBpZiB0aGUgcHJvdmlkZWQgY29sdW1uIG5hbWUgaXMgdmFsaWQNCiAgaWYgKCF4ICVpbiUgbmFtZXMoZGF0YSkpIHsNCiAgICBzdG9wKCJUaGUgc3BlY2lmaWVkIGNvbHVtbiBkb2VzIG5vdCBleGlzdCBpbiB0aGUgZGF0YSBmcmFtZS4iKQ0KICB9DQogIA0KICAjIERlZmluZSBhIGxpc3Qgb2YgZ2dwbG90IGxheWVycyBmb3IgZWFjaCBwbG90IHR5cGUNCiAgcGxvdF9sYXllcnMgPC0gbGlzdCgNCiAgICBrZGUgPSBnZW9tX2RlbnNpdHkoYWVzX3N0cmluZyh4ID0geCksIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSwNCiAgICBoaXN0ID0gZ2VvbV9oaXN0b2dyYW0oYWVzX3N0cmluZyh4ID0geCksIGZpbGwgPSAicmVkIiwgYWxwaGEgPSAwLjUpLA0KICAgIGRvdCA9IGdlb21fZG90cGxvdChhZXNfc3RyaW5nKHggPSB4KSwgYmluYXhpcyA9ICd4JywgZG90c2l6ZSA9IDEpDQogICkNCiAgDQogICMgR2V0IHRoZSBnZ3Bsb3QgbGF5ZXIgZnJvbSB0aGUgbGlzdCBiYXNlZCBvbiBwbG90X3R5cGUNCiAgbGF5ZXIgPC0gcGxvdF9sYXllcnNbW3Bsb3RfdHlwZV1dDQogIA0KICAjIENoZWNrIGlmIHBsb3RfdHlwZSBpcyB2YWxpZCBhbmQgbGF5ZXIgZXhpc3RzDQogIGlmIChpcy5udWxsKGxheWVyKSkgew0KICAgIHN0b3AoIlVua25vd24gcGxvdCB0eXBlLiBWYWxpZCBwbG90IHR5cGVzIGFyZTogJ2tkZScsICdoaXN0JywgJ2RvdCcuIikNCiAgfQ0KICANCiAgIyBDcmVhdGUgdGhlIGdncGxvdCBvYmplY3Qgd2l0aCB0aGUgc3BlY2lmaWVkIGxheWVyIGFuZCByZXR1cm4gaXQNCiAgcCA8LSBnZ3Bsb3QoZGF0YSkgKyBsYXllciArIGxhYnModGl0bGUgPSBwYXN0ZShwbG90X3R5cGUsICJvZiIsIHgpKQ0KICANCiAgcmV0dXJuKHApDQp9DQpgYGANCg0KVGEgY8OybiBjw7MgdGjhu4MgZMO5bmcgbmVzdGVkX2xpc3QgxJHhu4MgdHJ1eSB4deG6pXQgY+G6oyBsYXllciAoZnVuY3Rpb24pIHbDoCB0aXRsZSAoc3RyaW5nKSBjaG8gbeG7l2kgcGxvdF90eXBlDQoNCmBgYHtyfQ0KZGlzdF9wbG90XzJiIDwtIGZ1bmN0aW9uKGRhdGEsIHgsIHBsb3RfdHlwZSkgew0KICAjIFZhbGlkYXRlIGlucHV0DQogIGlmICgheCAlaW4lIG5hbWVzKGRhdGEpKSB7DQogICAgc3RvcCgiVGhlIHNwZWNpZmllZCBjb2x1bW4gZG9lcyBub3QgZXhpc3QgaW4gdGhlIGRhdGEgZnJhbWUuIikNCiAgfQ0KICANCiAgIyBNYXBwaW5nIG9mIHBsb3QgdHlwZXMgdG8gZ2dwbG90IGxheWVycyBhbmQgYWRkaXRpb25hbCBzZXR0aW5ncw0KICBwbG90X2xheWVycyA8LSBsaXN0KA0KICAgIGtkZSA9IGxpc3QobGF5ZXIgPSBnZW9tX2RlbnNpdHkoZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjUpLCB0aXRsZSA9ICJLZXJuZWwgRGVuc2l0eSBFc3RpbWF0ZSIpLA0KICAgIGhpc3QgPSBsaXN0KGxheWVyID0gZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJyZWQiLCBhbHBoYSA9IDAuNSksIHRpdGxlID0gIkhpc3RvZ3JhbSIpLA0KICAgIGRvdCA9IGxpc3QobGF5ZXIgPSBnZW9tX2RvdHBsb3QoYmluYXhpcyA9ICd4JywgZG90c2l6ZSA9IDAuNSksIHRpdGxlID0gIkRvdCBQbG90IikNCiAgKQ0KICANCiAgIyBSZXRyaWV2ZSB0aGUgcGxvdCBzZXR0aW5ncyBmb3IgdGhlIHNwZWNpZmllZCBwbG90X3R5cGUNCiAgcGxvdF9zZXR0aW5nIDwtIHBsb3RfbGF5ZXJzW1twbG90X3R5cGVdXQ0KICBpZiAoaXMubnVsbChwbG90X3NldHRpbmcpKSB7DQogICAgc3RvcCgiVW5rbm93biBwbG90IHR5cGUuIFZhbGlkIHBsb3QgdHlwZXMgYXJlOiAna2RlJywgJ2hpc3QnLCAnZG90Jy4iKQ0KICB9DQogIA0KICAjIENyZWF0ZSB0aGUgZ2dwbG90IG9iamVjdCBhbmQgYWRkIHRoZSBsYXllciBhbmQgdGl0bGUNCiAgcCA8LSBnZ3Bsb3QoZGF0YSwgYWVzX3N0cmluZyh4ID0geCkpICsNCiAgICBwbG90X3NldHRpbmckbGF5ZXIgKw0KICAgIGxhYnModGl0bGUgPSBwYXN0ZShwbG90X3NldHRpbmckdGl0bGUsICJvZiIsIHgpKQ0KICANCiAgcmV0dXJuKHApDQp9DQpgYGANCg0KTeG7mXQgY8OhY2ggdOG7lW5nIHF1w6F0LCB0YSBjw7MgdGjhu4MgZMO5bmcgbGlzdCDEkeG7gyBjaOG7qWEgY8OhYyBow6BtIGFub255bW91cyB24bubaSBu4buZaSBkdW5nIHTDuXkgdGjDrWNoIG5oxrAgc2F1Og0KDQpgYGB7cn0NCmRpc3RfcGxvdF8zIDwtIGZ1bmN0aW9uKGRhdGEsIHgsIHBsb3RfdHlwZSkgew0KICAjIENoZWNrIGlmIHRoZSBwcm92aWRlZCBjb2x1bW4gbmFtZSBpcyB2YWxpZA0KICBpZiAoIXggJWluJSBuYW1lcyhkYXRhKSkgew0KICAgIHN0b3AoIlRoZSBzcGVjaWZpZWQgY29sdW1uIGRvZXMgbm90IGV4aXN0IGluIHRoZSBkYXRhIGZyYW1lLiIpDQogIH0NCiAgDQogICMgRGVmaW5lIGEgbGlzdCBvZiBwbG90IHR5cGVzIGFuZCB0aGVpciBjb3JyZXNwb25kaW5nIGdncGxvdDIgZnVuY3Rpb25zDQogIHBsb3RfdHlwZXMgPC0gbGlzdCgNCiAgICAia2RlIiA9IGZ1bmN0aW9uKCkgZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcoeCA9IHgpKSArIA0KICAgICAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSwNCiAgICAiaGlzdCIgPSBmdW5jdGlvbigpIGdncGxvdChkYXRhLCBhZXNfc3RyaW5nKHggPSB4KSkgKyANCiAgICAgIGdlb21faGlzdG9ncmFtKGZpbGwgPSAicmVkIiwgYWxwaGEgPSAwLjUpLA0KICAgICJkb3QiID0gZnVuY3Rpb24oKSBnZ3Bsb3QoZGF0YSwgYWVzX3N0cmluZyh4ID0geCkpICsgDQogICAgICBnZW9tX2RvdHBsb3QoYmluYXhpcyA9ICd4JywgZG90c2l6ZSA9IDEpDQogICkNCiAgDQogICMgQ2hlY2sgaWYgdGhlIHBsb3QgdHlwZSBpcyB2YWxpZCBhbmQgZ2V0IHRoZSBjb3JyZXNwb25kaW5nIGZ1bmN0aW9uDQogIHBsb3RfZnVuYyA8LSBwbG90X3R5cGVzW1twbG90X3R5cGVdXQ0KICBpZiAoaXMubnVsbChwbG90X2Z1bmMpKSB7DQogICAgc3RvcCgiVW5rbm93biBwbG90IHR5cGUuIFZhbGlkIHBsb3QgdHlwZXMgYXJlOiAiLCBwYXN0ZShuYW1lcyhwbG90X3R5cGVzKSwgY29sbGFwc2UgPSAiLCAiKSkNCiAgfQ0KICANCiAgIyBHZW5lcmF0ZSBhbmQgcmV0dXJuIHRoZSBwbG90DQogIHAgPC0gcGxvdF9mdW5jKCkNCiAgcCArIGxhYnModGl0bGUgPSBwYXN0ZShwbG90X3R5cGUsICJQbG90IG9mIiwgeCkpDQp9DQpgYGANCg0KVGhheSB2w6wgZMO5bmcgdHLhu7FjIHRp4bq/cCBsaXN0LCB0cm9uZyBuZ8O0biBuZ+G7ryBSIGPDsm4gY8OzIGPhuqV1IHRyw7pjIMSRaeG7gXUga2nhu4duIHLhur0gbmjDoW5oIChzd2l0Y2gpLCBi4bqjbiBjaOG6pXQgY+G7p2EgbsOzIGPFqW5nIGzDoCBt4buZdCBsaXN0IGNo4bupYSBjw6FjIG9iamVjdCDEkcaw4bujYyB04bqhbyByYSB0cm9uZyBsw7pjIHRoaSBow6BuaCB0w7l5IHRoZW8gdOG7qyBraMOzYSDEkWnhu4F1IGtp4buHbg0KDQpgYGB7cn0NCmRpc3RfcGxvdF80IDwtIGZ1bmN0aW9uKGRhdGEsIHgsIHBsb3RfdHlwZSkgew0KICAjIENoZWNrIGlmIHRoZSBwcm92aWRlZCBjb2x1bW4gbmFtZSBpcyB2YWxpZA0KICBpZiAoIXggJWluJSBuYW1lcyhkYXRhKSkgew0KICAgIHN0b3AoIlRoZSBzcGVjaWZpZWQgY29sdW1uIGRvZXMgbm90IGV4aXN0IGluIHRoZSBkYXRhIGZyYW1lLiIpDQogIH0NCiAgDQogICMgRGVmaW5lIHRoZSBwbG90DQogIHAgPC0gZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcoeCA9IHgpKQ0KICANCiAgIyBHZW5lcmF0ZSB0aGUgcGxvdCBiYXNlZCBvbiB0aGUgdHlwZSB1c2luZyBzd2l0Y2gNCiAgcCA8LSBzd2l0Y2gocGxvdF90eXBlLA0KICAgICAgICAgICAgICBrZGUgPSBwICsgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArIA0KICAgICAgICAgICAgICAgIGxhYnModGl0bGUgPSBwYXN0ZSgiS2VybmVsIERlbnNpdHkgRXN0aW1hdGUgb2YiLCB4KSksDQogICAgICAgICAgICAgIGhpc3QgPSBwICsgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJyZWQiLCBhbHBoYSA9IDAuNSkgKyANCiAgICAgICAgICAgICAgICBsYWJzKHRpdGxlID0gcGFzdGUoIkhpc3RvZ3JhbSBvZiIsIHgpKSwNCiAgICAgICAgICAgICAgZG90ID0gcCArIGdlb21fZG90cGxvdChiaW5heGlzID0gJ3gnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb3RzaXplID0gMSkgKyANCiAgICAgICAgICAgICAgICBsYWJzKHRpdGxlID0gcGFzdGUoIkRvdCBQbG90IG9mIiwgeCkpLA0KICAgICAgICAgICAgICBzdG9wKCJVbmtub3duIHBsb3QgdHlwZS4gVmFsaWQgcGxvdCB0eXBlcyBhcmU6IGtkZSwgaGlzdCwgZG90IikNCiAgKQ0KICANCiAgIyBSZXR1cm4gdGhlIHBsb3QNCiAgcmV0dXJuKHApDQp9DQpgYGANCg0K