Data normalized across several flight logs, spanning from NS45 to NS80, excepting NS54, NS55, and NS68 (NS54 is reserved for a flight still in development, and both NS55 and NS68 failed to record location data).
Overall average calculations exclude the following flights due to failure or anomaly: NS56, NS74.
Sea level descent rate is averaged from descent rate below 300 meters.
Flight Records
Maximum Values by Flight
Maximum values for each recorded flight.
maximum_values_by_flight <-
data.frame(
Flight = flight_times$Flight,
Burst_Altitude_m = NA,
Landing_Distance_m = NA,
Ascent_Rate_m_s = NA,
Descent_Rate_m_s = NA,
Ground_Speed_m_s = NA
)
for (current_flight in levels(flight_data$Flight))
{
maximum_values_by_flight[maximum_values_by_flight$Flight == current_flight, 2:6] <- c(
mean(flight_data[flight_data$DateTime == flight_times[flight_times$Flight == current_flight, "Burst_Time"], "Altitude_m"], na.rm = TRUE),
mean(flight_data[flight_data$DateTime == flight_times[flight_times$Flight == current_flight, "Landing_Time"], "Downrange_Distance_m"], na.rm = TRUE),
max(flight_data[flight_data$Flight == current_flight, "Ascent_Rate_m_s"], na.rm = TRUE),
min(flight_data[flight_data$Flight == current_flight, "Ascent_Rate_m_s"], na.rm = TRUE),
max(flight_data[flight_data$Flight == current_flight, "Ground_Speed_m_s"], na.rm = TRUE)
)
}
maximum_values_by_flight[is.na(maximum_values_by_flight)] <- NA
rmarkdown::paged_table(maximum_values_by_flight)
ggplot(melt(maximum_values_by_flight, id.vars = "Flight"),
aes(
x = Flight,
y = value,
fill = rep_len(
as.factor(flight_times$Balloon_Mass_kg[flight_times$Flight %in% Flight]),
length.out = length(Flight)
)
)) + geom_col() + facet_grid(variable ~ ., scales = "free_y") +
theme(
legend.position = c(1.03, 1.03),
legend.direction = "horizontal",
legend.justification = "right",
axis.title.x = element_blank(),
axis.title.y = element_blank()
) + labs(title = "Maximum Values by Flight", fill = "Balloon Mass (kg)")
Program Records
Maximum values across all recorded flights.
overall_records <-
data.frame(
Record = c(
"Highest Altitude (m)",
"Farthest Downrange Distance (m)",
"Fastest Ascent Rate (m/s)",
"Fastest Descent Rate (m/s)",
"Fastest Ground Speed (m/s)"
),
Flight = factor(levels = levels(flight_times$Flight), NA),
Value = numeric(5),
DateTime = as.POSIXct(numeric(5), origin = "1970-01-01")
)
overall_records[1, 2:3] <-
c(as.character(flight_data[flight_data$Altitude_m == max(flight_data$Altitude_m, na.rm = TRUE), "Flight"]), round(max(flight_data$Altitude_m, na.rm = TRUE)))
overall_records[1, 4] <-
as.POSIXct(flight_data[flight_data$Altitude_m == max(flight_data$Altitude_m, na.rm = TRUE), "DateTime"], tz = Sys.timezone())
overall_records[2, 2:3] <-
c(as.character(flight_data[flight_data$Downrange_Distance_m == max(flight_data$Downrange_Distance_m, na.rm = TRUE), "Flight"]), round(max(flight_data$Downrange_Distance_m, na.rm = TRUE)))
overall_records[2, 4] <-
as.POSIXct(flight_data[flight_data$Downrange_Distance_m == max(flight_data$Downrange_Distance_m, na.rm = TRUE), "DateTime"], tz = Sys.timezone())
overall_records[3, 2:3] <-
c(as.character(flight_data[flight_data$Ascent_Rate_m_s == max(flight_data$Ascent_Rate_m_s, na.rm = TRUE), "Flight"]), round(max(flight_data$Ascent_Rate_m_s, na.rm = TRUE)))
overall_records[3, 4] <-
as.POSIXct(flight_data[flight_data$Ascent_Rate_m_s == max(flight_data$Ascent_Rate_m_s, na.rm = TRUE), "DateTime"], tz = Sys.timezone())
overall_records[4, 2:3] <-
c(as.character(flight_data[flight_data$Ascent_Rate_m_s == min(flight_data$Ascent_Rate_m_s, na.rm = TRUE), "Flight"]), round(min(flight_data$Ascent_Rate_m_s, na.rm = TRUE)))
overall_records[4, 4] <-
as.POSIXct(flight_data[flight_data$Ascent_Rate_m_s == min(flight_data$Ascent_Rate_m_s, na.rm = TRUE), "DateTime"], tz = Sys.timezone())
overall_records[5, 2:3] <-
c(as.character(flight_data[flight_data$Ground_Speed_m_s == max(flight_data$Ground_Speed_m_s, na.rm = TRUE), "Flight"]), round(max(flight_data$Ground_Speed_m_s, na.rm = TRUE)))
overall_records[5, 4] <-
as.POSIXct(flight_data[flight_data$Ground_Speed_m_s == max(flight_data$Ground_Speed_m_s, na.rm = TRUE), "DateTime"], tz = Sys.timezone())
rmarkdown::paged_table(overall_records)
Averages
Overall Averages
overall_averages <-
data.frame(
Statistic = c(
"Burst Altitude (m)",
"Landing Distance (m)",
"Ascent Rate (m/s)",
"Descent Rate (m/s)",
"Ground Speed (m/s)",
"Sea Level Descent Rate (m/s)"
),
Mean = 0
)
overall_averages[overall_averages$Statistic == "Burst Altitude (m)", "Mean"] <-
mean(maximum_values_by_flight$Burst_Altitude_m[!(maximum_values_by_flight$Flight %in% average_excluded_flights)], na.rm = TRUE)
overall_averages[overall_averages$Statistic == "Landing Distance (m)", "Mean"] <-
mean(maximum_values_by_flight$Landing_Distance_m[!(maximum_values_by_flight$Flight %in% average_excluded_flights)], na.rm = TRUE)
overall_averages[overall_averages$Statistic == "Ascent Rate (m/s)", "Mean"] <-
mean(flight_data$Ascent_Rate_m_s[flight_data$Ascent_Rate_m_s > 0 &
!(flight_data$Flight %in% average_excluded_flights)], na.rm = TRUE)
overall_averages[overall_averages$Statistic == "Descent Rate (m/s)", "Mean"] <-
mean(flight_data$Ascent_Rate_m_s[flight_data$Ascent_Rate_m_s < 0 &
!(flight_data$Flight %in% average_excluded_flights)], na.rm = TRUE)
overall_averages[overall_averages$Statistic == "Ground Speed (m/s)", "Mean"] <-
mean(flight_data$Ground_Speed_m_s[!(flight_data$Flight %in% average_excluded_flights)], na.rm = TRUE)
overall_averages[overall_averages$Statistic == "Sea Level Descent Rate (m/s)", "Mean"] <-
mean(flight_data$Ascent_Rate_m_s[flight_data$Altitude_m < 300 &
flight_data$Ascent_Rate_m_s < 0 &
flight_data$Ground_Speed_m_s > 2 &
!(flight_data$Flight %in% average_excluded_flights)], na.rm = TRUE)
rmarkdown::paged_table(overall_averages)
excluding NS56, NS74
Averages by Flight
averages_by_flight <-
data.frame(
Flight = flight_times$Flight,
Ascent_Rate_m_s = 0,
Descent_Rate_m_s = 0,
Ground_Speed_m_s = 0,
Sea_Level_Descent_Rate_m_s = 0
)
for (current_flight in averages_by_flight$Flight)
{
averages_by_flight[averages_by_flight$Flight == current_flight, 2:5] <-
c(
mean(flight_data$Ascent_Rate_m_s[flight_data$Flight == current_flight &
flight_data$Ascent_Rate_m_s > 0], na.rm = TRUE),
mean(flight_data$Ascent_Rate_m_s[flight_data$Flight == current_flight &
flight_data$Ascent_Rate_m_s < 0], na.rm = TRUE),
mean(flight_data$Ground_Speed_m_s[flight_data$Flight == current_flight], na.rm = TRUE),
mean(flight_data$Ascent_Rate_m_s[flight_data$Ascent_Rate_m_s < 0 &
flight_data$Altitude_m < sea_level_descent_rate_threshold &
flight_data$Ground_Speed_m_s > 2 &
flight_data$Flight %in% current_flight], na.rm = TRUE)
)
}
rm(current_flight)
averages_by_flight[is.na(averages_by_flight)] <- NA
rmarkdown::paged_table(averages_by_flight)
ggplot(melt(averages_by_flight,
id.vars = "Flight"),
aes(
x = Flight,
y = value,
fill = rep_len(
as.factor(flight_times$Balloon_Mass_kg[flight_times$Flight %in% Flight]),
length.out = length(Flight)
)
)) +
geom_col() +
facet_grid(variable ~ ., scales = "free_y") +
labs(title = "Averages by Flight", fill = "Balloon Mass (kg)") +
theme(
legend.position = c(1.03, 1.03),
legend.direction = "horizontal",
legend.justification = "right",
axis.title.x = element_blank(),
axis.title.y = element_blank()
)
ggplot(averages_by_flight,
aes(
x = as.Date(format(flight_times$Date, format = "%m-%d"), format = "%m-%d"),
y = Ground_Speed_m_s,
fill = rep_len(
as.factor(flight_times$Balloon_Mass_kg[flight_times$Flight %in% Flight]),
length.out = length(Flight)
)
)) +
geom_col(width = 2, position = "dodge") +
geom_point(size = 0.1) +
scale_x_date(
date_breaks = "1 month",
labels = date_format("%b"),
limits = as.Date(c("01-01", "12-31"), format = "%m-%d"),
expand = c(0, 0)
) +
geom_dl(aes(label = gsub("NS", "", Flight)),
method = list(cex = 0.8, dl.trans(y = y + 0.2), "top.bumpup")) +
labs(title = "Average Ground Speed vs Time of Year",
x = "Time of Year",
y = "Average Ground Speed (m/s)",
fill = "Balloon Mass (kg)") +
theme(
legend.position = c(1, 1.03),
legend.direction = "horizontal",
legend.justification = "right"
)
Averages by Balloon Mass
averages_by_balloon_mass <-
data.frame(
Balloon_Mass_kg = unique(flight_times$Balloon_Mass_kg),
Burst_Altitude_m = 0,
Landing_Distance_m = 0,
Ascent_Rate_m_s = 0,
Descent_Rate_m_s = 0,
Ground_Speed_m_s = 0,
Sea_Level_Descent_Rate_m_s = 0
)
averages_by_balloon_mass <-
averages_by_balloon_mass[complete.cases(averages_by_balloon_mass$Balloon_Mass_kg),]
averages_by_balloon_mass <-
averages_by_balloon_mass[order(averages_by_balloon_mass$Balloon_Mass_kg), ]
row.names(averages_by_balloon_mass) <- NULL
for (current_balloon_mass in averages_by_balloon_mass$Balloon_Mass_kg)
{
current_flights <-
flight_times[!(flight_times$Flight %in% average_excluded_flights) &
flight_times$Balloon_Mass_kg == current_balloon_mass &
!is.na(flight_times$Balloon_Mass_kg), "Flight"]
averages_by_balloon_mass[averages_by_balloon_mass$Balloon_Mass_kg == current_balloon_mass, 2:7] <-
c(
mean(maximum_values_by_flight$Burst_Altitude_m[maximum_values_by_flight$Flight %in% current_flights], na.rm = TRUE),
mean(maximum_values_by_flight$Landing_Distance_m[maximum_values_by_flight$Flight %in% current_flights], na.rm = TRUE),
mean(averages_by_flight$Ascent_Rate_m_s[averages_by_flight$Flight %in% current_flights], na.rm = TRUE),
mean(averages_by_flight$Descent_Rate_m_s[averages_by_flight$Flight %in% current_flights], na.rm = TRUE),
mean(averages_by_flight$Ground_Speed_m_s[averages_by_flight$Flight %in% current_flights], na.rm = TRUE),
mean(averages_by_flight$Sea_Level_Descent_Rate_m_s[averages_by_flight$Flight %in% current_flights], na.rm = TRUE)
)
}
rm(current_balloon_mass, current_flights)
rmarkdown::paged_table(averages_by_balloon_mass)
ggplot(
melt(averages_by_balloon_mass,
id.vars = "Balloon_Mass_kg"),
aes(
x = as.factor(Balloon_Mass_kg),
y = value,
fill = as.factor(Balloon_Mass_kg)
)
) +
geom_col() +
facet_wrap(facets = "variable", scales = "free_y") +
labs(title = "Averages by Balloon Mass", fill = "Balloon Mass (kg)") +
theme(
legend.position = c(1, 1.07),
legend.direction = "horizontal",
legend.justification = "right",
axis.title.x = element_blank(),
axis.title.y = element_blank()
)
excluding NS56, NS74
Mass Ratios
mass_ratios <- flight_times[c(1, 7:9)]
mass_ratios$Balloon_Mass_Ratio <-
mass_ratios$Balloon_Mass_kg / mass_ratios$Total_Mass_kg
mass_ratios$Helium_Mass_Ratio <-
mass_ratios$Helium_Mass_kg / mass_ratios$Total_Mass_kg
mass_ratios$Helium_Balloon_Ratio <-
mass_ratios$Helium_Mass_kg / mass_ratios$Balloon_Mass_kg
rmarkdown::paged_table(mass_ratios)
Mass vs Flight Statistics
ggplot(
melt(
melt(
merge(averages_by_flight[c(1, 2, 5)],
mass_ratios[c(1, 2:4)],
by = c("Flight")),
id.vars = c(
"Flight",
"Balloon_Mass_kg",
"Ascent_Rate_m_s",
"Sea_Level_Descent_Rate_m_s"
),
variable.name = "mass_type",
value.name = "mass"
),
id.vars = c("Flight", "Balloon_Mass_kg", "mass_type", "mass"),
variable.name = "value_type",
value.name = "value"
),
aes(
x = mass,
y = value,
colour = as.factor(Balloon_Mass_kg)
)
) +
geom_point(size = 0.1) +
facet_grid(value_type ~ mass_type, scales = "free") +
geom_dl(aes(label = gsub("NS", "", Flight)),
method = list(cex = 0.8, "visualcenter", "bumpup")) +
theme(
legend.position = c(1.03, 1.07),
legend.direction = "horizontal",
legend.justification = "right",
axis.title.x = element_blank(),
axis.title.y = element_blank()
) +
guides(col = guide_legend(override.aes = list(shape = 15, size = 7))) +
labs(title = "Mass vs Flight Statistics", colour = "Balloon Mass (kg)")
Mass Ratios vs Flight Statistics
ggplot(
melt(
melt(
merge(
merge(averages_by_flight[c(1, 2)],
mass_ratios[c(1:4)],
by = "Flight"),
maximum_values_by_flight[c(1, 2)],
by = "Flight"
),
id.vars = c(
"Flight",
"Balloon_Mass_kg",
"Ascent_Rate_m_s",
"Burst_Altitude_m"
),
variable.name = "ratio_type",
value.name = "ratio"
),
id.vars = c("Flight", "Balloon_Mass_kg", "ratio_type", "ratio"),
variable.name = "value_type",
value.name = "value"
),
aes(
x = ratio,
y = value,
colour = as.factor(Balloon_Mass_kg)
)
) +
geom_point(size = 0.1) +
facet_grid(value_type ~ ratio_type, scales = "free") +
geom_dl(aes(label = gsub("NS", "", Flight)),
method = list(cex = 0.8, "visualcenter", "bumpup")) +
theme(
legend.position = c(1.03, 1.07),
legend.direction = "horizontal",
legend.justification = "right",
axis.title.x = element_blank(),
axis.title.y = element_blank()
) +
guides(col = guide_legend(override.aes = list(shape = 15, size = 7))) +
labs(title = "Mass Ratios vs Flight Statistics", colour = "Balloon Mass (kg)")
LS0tCnRpdGxlOiAiRmxpZ2h0IFN0YXRpc3RpY3MiCm91dHB1dDoKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICAgIGNvbGxhcHNlZDogZmFsc2UKLS0tCgpbRmxpZ2h0IFN1bW1hcnldKGZsaWdodF9zdW1tYXJ5Lm5iLmh0bWwpCgpbRmxpZ2h0IEFuYWx5c2lzXShmbGlnaHRfYW5hbHlzaXMubmIuaHRtbCkKCkRhdGEgbm9ybWFsaXplZCBhY3Jvc3Mgc2V2ZXJhbCBmbGlnaHQgbG9ncywgc3Bhbm5pbmcgZnJvbSBgciBtaW4obGV2ZWxzKGZsaWdodF9kYXRhJEZsaWdodCkpYCB0byBgciBtYXgobGV2ZWxzKGZsaWdodF9kYXRhJEZsaWdodCkpYCwgZXhjZXB0aW5nIE5TNTQsIE5TNTUsIGFuZCBOUzY4IChOUzU0IGlzIHJlc2VydmVkIGZvciBhIGZsaWdodCBzdGlsbCBpbiBkZXZlbG9wbWVudCwgYW5kIGJvdGggTlM1NSBhbmQgTlM2OCBmYWlsZWQgdG8gcmVjb3JkIGxvY2F0aW9uIGRhdGEpLgoKT3ZlcmFsbCBhdmVyYWdlIGNhbGN1bGF0aW9ucyBleGNsdWRlIHRoZSBmb2xsb3dpbmcgZmxpZ2h0cyBkdWUgdG8gZmFpbHVyZSBvciBhbm9tYWx5OiBgciBhdmVyYWdlX2V4Y2x1ZGVkX2ZsaWdodHNgLgoKU2VhIGxldmVsIGRlc2NlbnQgcmF0ZSBpcyBhdmVyYWdlZCBmcm9tIGRlc2NlbnQgcmF0ZSBiZWxvdyBgciBzZWFfbGV2ZWxfZGVzY2VudF9yYXRlX3RocmVzaG9sZGAgbWV0ZXJzLgoKYGBge3Igc2V0dXB9CnJlcXVpcmUoZ2dwbG90MikKcmVxdWlyZShkaXJlY3RsYWJlbHMpCnJlcXVpcmUocmVzaGFwZTIpCnJlcXVpcmUoc2NhbGVzKQoKc291cmNlKCJzZXR1cC5SIikKCmZsaWdodF90aW1lcyA8LSByZWFkX2ZsaWdodF90aW1lcygiZmxpZ2h0X3RpbWVzLnR4dCIpCmZsaWdodF9kYXRhIDwtIHJlYWRfZmxpZ2h0X2RhdGEoImZsaWdodF9kYXRhLyIpCgphdmVyYWdlX2V4Y2x1ZGVkX2ZsaWdodHMgPC0gYygiTlM1NiIsICJOUzc0IikKc2VhX2xldmVsX2Rlc2NlbnRfcmF0ZV90aHJlc2hvbGQgPSAzMDAKYGBgCgojIEZsaWdodCBSZWNvcmRzCgojIyBNYXhpbXVtIFZhbHVlcyBieSBGbGlnaHQKCk1heGltdW0gdmFsdWVzIGZvciBlYWNoIHJlY29yZGVkIGZsaWdodC4KCmBgYHtyIG1heGltdW1fdmFsdWVzX2J5X2ZsaWdodCwgZmlnLmhlaWdodD03LjQxNjQwNzksIGZpZy53aWR0aD0xMn0KbWF4aW11bV92YWx1ZXNfYnlfZmxpZ2h0IDwtCiAgICBkYXRhLmZyYW1lKAogICAgICAgIEZsaWdodCA9IGZsaWdodF90aW1lcyRGbGlnaHQsCiAgICAgICAgQnVyc3RfQWx0aXR1ZGVfbSA9IE5BLAogICAgICAgIExhbmRpbmdfRGlzdGFuY2VfbSA9IE5BLAogICAgICAgIEFzY2VudF9SYXRlX21fcyA9IE5BLAogICAgICAgIERlc2NlbnRfUmF0ZV9tX3MgPSBOQSwKICAgICAgICBHcm91bmRfU3BlZWRfbV9zID0gTkEKICAgICkKCmZvciAoY3VycmVudF9mbGlnaHQgaW4gbGV2ZWxzKGZsaWdodF9kYXRhJEZsaWdodCkpCnsKICAgIG1heGltdW1fdmFsdWVzX2J5X2ZsaWdodFttYXhpbXVtX3ZhbHVlc19ieV9mbGlnaHQkRmxpZ2h0ID09IGN1cnJlbnRfZmxpZ2h0LCAyOjZdIDwtIGMoCiAgICAgICAgbWVhbihmbGlnaHRfZGF0YVtmbGlnaHRfZGF0YSREYXRlVGltZSA9PSBmbGlnaHRfdGltZXNbZmxpZ2h0X3RpbWVzJEZsaWdodCA9PSBjdXJyZW50X2ZsaWdodCwgIkJ1cnN0X1RpbWUiXSwgIkFsdGl0dWRlX20iXSwgbmEucm0gPSBUUlVFKSwKICAgICAgICBtZWFuKGZsaWdodF9kYXRhW2ZsaWdodF9kYXRhJERhdGVUaW1lID09IGZsaWdodF90aW1lc1tmbGlnaHRfdGltZXMkRmxpZ2h0ID09IGN1cnJlbnRfZmxpZ2h0LCAiTGFuZGluZ19UaW1lIl0sICJEb3ducmFuZ2VfRGlzdGFuY2VfbSJdLCBuYS5ybSA9IFRSVUUpLAogICAgICAgIG1heChmbGlnaHRfZGF0YVtmbGlnaHRfZGF0YSRGbGlnaHQgPT0gY3VycmVudF9mbGlnaHQsICJBc2NlbnRfUmF0ZV9tX3MiXSwgbmEucm0gPSBUUlVFKSwKICAgICAgICBtaW4oZmxpZ2h0X2RhdGFbZmxpZ2h0X2RhdGEkRmxpZ2h0ID09IGN1cnJlbnRfZmxpZ2h0LCAiQXNjZW50X1JhdGVfbV9zIl0sIG5hLnJtID0gVFJVRSksCiAgICAgICAgbWF4KGZsaWdodF9kYXRhW2ZsaWdodF9kYXRhJEZsaWdodCA9PSBjdXJyZW50X2ZsaWdodCwgIkdyb3VuZF9TcGVlZF9tX3MiXSwgbmEucm0gPSBUUlVFKQogICAgKQp9CgptYXhpbXVtX3ZhbHVlc19ieV9mbGlnaHRbaXMubmEobWF4aW11bV92YWx1ZXNfYnlfZmxpZ2h0KV0gPC0gTkEKCnJtYXJrZG93bjo6cGFnZWRfdGFibGUobWF4aW11bV92YWx1ZXNfYnlfZmxpZ2h0KQoKZ2dwbG90KG1lbHQobWF4aW11bV92YWx1ZXNfYnlfZmxpZ2h0LCBpZC52YXJzID0gIkZsaWdodCIpLAogICAgICAgYWVzKAogICAgICAgICAgIHggPSBGbGlnaHQsCiAgICAgICAgICAgeSA9IHZhbHVlLAogICAgICAgICAgIGZpbGwgPSByZXBfbGVuKAogICAgICAgICAgICAgICBhcy5mYWN0b3IoZmxpZ2h0X3RpbWVzJEJhbGxvb25fTWFzc19rZ1tmbGlnaHRfdGltZXMkRmxpZ2h0ICVpbiUgRmxpZ2h0XSksCiAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSBsZW5ndGgoRmxpZ2h0KQogICAgICAgICAgICkKICAgICAgICkpICsgZ2VvbV9jb2woKSArIGZhY2V0X2dyaWQodmFyaWFibGUgfiAuLCBzY2FsZXMgPSAiZnJlZV95IikgKwogICAgdGhlbWUoCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygxLjAzLCAxLjAzKSwKICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLAogICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gInJpZ2h0IiwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpCiAgICApICsgbGFicyh0aXRsZSA9ICJNYXhpbXVtIFZhbHVlcyBieSBGbGlnaHQiLCBmaWxsID0gIkJhbGxvb24gTWFzcyAoa2cpIikKYGBgCgojIyBQcm9ncmFtIFJlY29yZHMKCk1heGltdW0gdmFsdWVzIGFjcm9zcyBhbGwgcmVjb3JkZWQgZmxpZ2h0cy4KCmBgYHtyIG92ZXJhbGxfcmVjb3Jkc30Kb3ZlcmFsbF9yZWNvcmRzIDwtCiAgICBkYXRhLmZyYW1lKAogICAgICAgIFJlY29yZCA9IGMoCiAgICAgICAgICAgICJIaWdoZXN0IEFsdGl0dWRlIChtKSIsCiAgICAgICAgICAgICJGYXJ0aGVzdCBEb3ducmFuZ2UgRGlzdGFuY2UgKG0pIiwKICAgICAgICAgICAgIkZhc3Rlc3QgQXNjZW50IFJhdGUgKG0vcykiLAogICAgICAgICAgICAiRmFzdGVzdCBEZXNjZW50IFJhdGUgKG0vcykiLAogICAgICAgICAgICAiRmFzdGVzdCBHcm91bmQgU3BlZWQgKG0vcykiCiAgICAgICAgKSwKICAgICAgICBGbGlnaHQgPSBmYWN0b3IobGV2ZWxzID0gbGV2ZWxzKGZsaWdodF90aW1lcyRGbGlnaHQpLCBOQSksCiAgICAgICAgVmFsdWUgPSBudW1lcmljKDUpLAogICAgICAgIERhdGVUaW1lID0gYXMuUE9TSVhjdChudW1lcmljKDUpLCBvcmlnaW4gPSAiMTk3MC0wMS0wMSIpCiAgICApCgpvdmVyYWxsX3JlY29yZHNbMSwgMjozXSA8LQogICAgYyhhcy5jaGFyYWN0ZXIoZmxpZ2h0X2RhdGFbZmxpZ2h0X2RhdGEkQWx0aXR1ZGVfbSA9PSBtYXgoZmxpZ2h0X2RhdGEkQWx0aXR1ZGVfbSwgbmEucm0gPSBUUlVFKSwgIkZsaWdodCJdKSwgcm91bmQobWF4KGZsaWdodF9kYXRhJEFsdGl0dWRlX20sIG5hLnJtID0gVFJVRSkpKQoKb3ZlcmFsbF9yZWNvcmRzWzEsIDRdIDwtCiAgICBhcy5QT1NJWGN0KGZsaWdodF9kYXRhW2ZsaWdodF9kYXRhJEFsdGl0dWRlX20gPT0gbWF4KGZsaWdodF9kYXRhJEFsdGl0dWRlX20sIG5hLnJtID0gVFJVRSksICJEYXRlVGltZSJdLCB0eiA9IFN5cy50aW1lem9uZSgpKQoKb3ZlcmFsbF9yZWNvcmRzWzIsIDI6M10gPC0KICAgIGMoYXMuY2hhcmFjdGVyKGZsaWdodF9kYXRhW2ZsaWdodF9kYXRhJERvd25yYW5nZV9EaXN0YW5jZV9tID09IG1heChmbGlnaHRfZGF0YSREb3ducmFuZ2VfRGlzdGFuY2VfbSwgbmEucm0gPSBUUlVFKSwgIkZsaWdodCJdKSwgcm91bmQobWF4KGZsaWdodF9kYXRhJERvd25yYW5nZV9EaXN0YW5jZV9tLCBuYS5ybSA9IFRSVUUpKSkKCm92ZXJhbGxfcmVjb3Jkc1syLCA0XSA8LQogICAgYXMuUE9TSVhjdChmbGlnaHRfZGF0YVtmbGlnaHRfZGF0YSREb3ducmFuZ2VfRGlzdGFuY2VfbSA9PSBtYXgoZmxpZ2h0X2RhdGEkRG93bnJhbmdlX0Rpc3RhbmNlX20sIG5hLnJtID0gVFJVRSksICJEYXRlVGltZSJdLCB0eiA9IFN5cy50aW1lem9uZSgpKQoKb3ZlcmFsbF9yZWNvcmRzWzMsIDI6M10gPC0KICAgIGMoYXMuY2hhcmFjdGVyKGZsaWdodF9kYXRhW2ZsaWdodF9kYXRhJEFzY2VudF9SYXRlX21fcyA9PSBtYXgoZmxpZ2h0X2RhdGEkQXNjZW50X1JhdGVfbV9zLCBuYS5ybSA9IFRSVUUpLCAiRmxpZ2h0Il0pLCByb3VuZChtYXgoZmxpZ2h0X2RhdGEkQXNjZW50X1JhdGVfbV9zLCBuYS5ybSA9IFRSVUUpKSkKCm92ZXJhbGxfcmVjb3Jkc1szLCA0XSA8LQogICAgYXMuUE9TSVhjdChmbGlnaHRfZGF0YVtmbGlnaHRfZGF0YSRBc2NlbnRfUmF0ZV9tX3MgPT0gbWF4KGZsaWdodF9kYXRhJEFzY2VudF9SYXRlX21fcywgbmEucm0gPSBUUlVFKSwgIkRhdGVUaW1lIl0sIHR6ID0gU3lzLnRpbWV6b25lKCkpCgpvdmVyYWxsX3JlY29yZHNbNCwgMjozXSA8LQogICAgYyhhcy5jaGFyYWN0ZXIoZmxpZ2h0X2RhdGFbZmxpZ2h0X2RhdGEkQXNjZW50X1JhdGVfbV9zID09IG1pbihmbGlnaHRfZGF0YSRBc2NlbnRfUmF0ZV9tX3MsIG5hLnJtID0gVFJVRSksICJGbGlnaHQiXSksIHJvdW5kKG1pbihmbGlnaHRfZGF0YSRBc2NlbnRfUmF0ZV9tX3MsIG5hLnJtID0gVFJVRSkpKQoKb3ZlcmFsbF9yZWNvcmRzWzQsIDRdIDwtCiAgICBhcy5QT1NJWGN0KGZsaWdodF9kYXRhW2ZsaWdodF9kYXRhJEFzY2VudF9SYXRlX21fcyA9PSBtaW4oZmxpZ2h0X2RhdGEkQXNjZW50X1JhdGVfbV9zLCBuYS5ybSA9IFRSVUUpLCAiRGF0ZVRpbWUiXSwgdHogPSBTeXMudGltZXpvbmUoKSkKCm92ZXJhbGxfcmVjb3Jkc1s1LCAyOjNdIDwtCiAgICBjKGFzLmNoYXJhY3RlcihmbGlnaHRfZGF0YVtmbGlnaHRfZGF0YSRHcm91bmRfU3BlZWRfbV9zID09IG1heChmbGlnaHRfZGF0YSRHcm91bmRfU3BlZWRfbV9zLCBuYS5ybSA9IFRSVUUpLCAiRmxpZ2h0Il0pLCByb3VuZChtYXgoZmxpZ2h0X2RhdGEkR3JvdW5kX1NwZWVkX21fcywgbmEucm0gPSBUUlVFKSkpCgpvdmVyYWxsX3JlY29yZHNbNSwgNF0gPC0KICAgIGFzLlBPU0lYY3QoZmxpZ2h0X2RhdGFbZmxpZ2h0X2RhdGEkR3JvdW5kX1NwZWVkX21fcyA9PSBtYXgoZmxpZ2h0X2RhdGEkR3JvdW5kX1NwZWVkX21fcywgbmEucm0gPSBUUlVFKSwgIkRhdGVUaW1lIl0sIHR6ID0gU3lzLnRpbWV6b25lKCkpCgpybWFya2Rvd246OnBhZ2VkX3RhYmxlKG92ZXJhbGxfcmVjb3JkcykKYGBgCgojIEF2ZXJhZ2VzCgojIyBPdmVyYWxsIEF2ZXJhZ2VzCgpgYGB7ciBvdmVyYWxsX2F2ZXJhZ2VzfQpvdmVyYWxsX2F2ZXJhZ2VzIDwtCiAgICBkYXRhLmZyYW1lKAogICAgICAgIFN0YXRpc3RpYyA9IGMoCiAgICAgICAgICAgICJCdXJzdCBBbHRpdHVkZSAobSkiLAogICAgICAgICAgICAiTGFuZGluZyBEaXN0YW5jZSAobSkiLAogICAgICAgICAgICAiQXNjZW50IFJhdGUgKG0vcykiLAogICAgICAgICAgICAiRGVzY2VudCBSYXRlIChtL3MpIiwKICAgICAgICAgICAgIkdyb3VuZCBTcGVlZCAobS9zKSIsCiAgICAgICAgICAgICJTZWEgTGV2ZWwgRGVzY2VudCBSYXRlIChtL3MpIgogICAgICAgICksCiAgICAgICAgTWVhbiA9IDAKICAgICkKCm92ZXJhbGxfYXZlcmFnZXNbb3ZlcmFsbF9hdmVyYWdlcyRTdGF0aXN0aWMgPT0gIkJ1cnN0IEFsdGl0dWRlIChtKSIsICJNZWFuIl0gPC0KICAgIG1lYW4obWF4aW11bV92YWx1ZXNfYnlfZmxpZ2h0JEJ1cnN0X0FsdGl0dWRlX21bIShtYXhpbXVtX3ZhbHVlc19ieV9mbGlnaHQkRmxpZ2h0ICVpbiUgYXZlcmFnZV9leGNsdWRlZF9mbGlnaHRzKV0sIG5hLnJtID0gVFJVRSkKCm92ZXJhbGxfYXZlcmFnZXNbb3ZlcmFsbF9hdmVyYWdlcyRTdGF0aXN0aWMgPT0gIkxhbmRpbmcgRGlzdGFuY2UgKG0pIiwgIk1lYW4iXSA8LQogICAgbWVhbihtYXhpbXVtX3ZhbHVlc19ieV9mbGlnaHQkTGFuZGluZ19EaXN0YW5jZV9tWyEobWF4aW11bV92YWx1ZXNfYnlfZmxpZ2h0JEZsaWdodCAlaW4lIGF2ZXJhZ2VfZXhjbHVkZWRfZmxpZ2h0cyldLCBuYS5ybSA9IFRSVUUpCgpvdmVyYWxsX2F2ZXJhZ2VzW292ZXJhbGxfYXZlcmFnZXMkU3RhdGlzdGljID09ICJBc2NlbnQgUmF0ZSAobS9zKSIsICJNZWFuIl0gPC0KICAgIG1lYW4oZmxpZ2h0X2RhdGEkQXNjZW50X1JhdGVfbV9zW2ZsaWdodF9kYXRhJEFzY2VudF9SYXRlX21fcyA+IDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICEoZmxpZ2h0X2RhdGEkRmxpZ2h0ICVpbiUgYXZlcmFnZV9leGNsdWRlZF9mbGlnaHRzKV0sIG5hLnJtID0gVFJVRSkKb3ZlcmFsbF9hdmVyYWdlc1tvdmVyYWxsX2F2ZXJhZ2VzJFN0YXRpc3RpYyA9PSAiRGVzY2VudCBSYXRlIChtL3MpIiwgIk1lYW4iXSA8LQogICAgbWVhbihmbGlnaHRfZGF0YSRBc2NlbnRfUmF0ZV9tX3NbZmxpZ2h0X2RhdGEkQXNjZW50X1JhdGVfbV9zIDwgMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIShmbGlnaHRfZGF0YSRGbGlnaHQgJWluJSBhdmVyYWdlX2V4Y2x1ZGVkX2ZsaWdodHMpXSwgbmEucm0gPSBUUlVFKQpvdmVyYWxsX2F2ZXJhZ2VzW292ZXJhbGxfYXZlcmFnZXMkU3RhdGlzdGljID09ICJHcm91bmQgU3BlZWQgKG0vcykiLCAiTWVhbiJdIDwtCiAgICBtZWFuKGZsaWdodF9kYXRhJEdyb3VuZF9TcGVlZF9tX3NbIShmbGlnaHRfZGF0YSRGbGlnaHQgJWluJSBhdmVyYWdlX2V4Y2x1ZGVkX2ZsaWdodHMpXSwgbmEucm0gPSBUUlVFKQoKb3ZlcmFsbF9hdmVyYWdlc1tvdmVyYWxsX2F2ZXJhZ2VzJFN0YXRpc3RpYyA9PSAiU2VhIExldmVsIERlc2NlbnQgUmF0ZSAobS9zKSIsICJNZWFuIl0gPC0KICAgIG1lYW4oZmxpZ2h0X2RhdGEkQXNjZW50X1JhdGVfbV9zW2ZsaWdodF9kYXRhJEFsdGl0dWRlX20gPCAzMDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZsaWdodF9kYXRhJEFzY2VudF9SYXRlX21fcyA8IDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZsaWdodF9kYXRhJEdyb3VuZF9TcGVlZF9tX3MgPiAyICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhKGZsaWdodF9kYXRhJEZsaWdodCAlaW4lIGF2ZXJhZ2VfZXhjbHVkZWRfZmxpZ2h0cyldLCBuYS5ybSA9IFRSVUUpCgpybWFya2Rvd246OnBhZ2VkX3RhYmxlKG92ZXJhbGxfYXZlcmFnZXMpCmBgYApleGNsdWRpbmcgYHIgYXZlcmFnZV9leGNsdWRlZF9mbGlnaHRzYAoKIyMgQXZlcmFnZXMgYnkgRmxpZ2h0CgpgYGB7ciBhdmVyYWdlc19ieV9mbGlnaHQsIGZpZy5oZWlnaHQ9Ny40MTY0MDc5LCBmaWcud2lkdGg9MTJ9CmF2ZXJhZ2VzX2J5X2ZsaWdodCA8LQogICAgZGF0YS5mcmFtZSgKICAgICAgICBGbGlnaHQgPSBmbGlnaHRfdGltZXMkRmxpZ2h0LAogICAgICAgIEFzY2VudF9SYXRlX21fcyA9IDAsCiAgICAgICAgRGVzY2VudF9SYXRlX21fcyA9IDAsCiAgICAgICAgR3JvdW5kX1NwZWVkX21fcyA9IDAsCiAgICAgICAgU2VhX0xldmVsX0Rlc2NlbnRfUmF0ZV9tX3MgPSAwCiAgICApCgpmb3IgKGN1cnJlbnRfZmxpZ2h0IGluIGF2ZXJhZ2VzX2J5X2ZsaWdodCRGbGlnaHQpCnsKICAgIGF2ZXJhZ2VzX2J5X2ZsaWdodFthdmVyYWdlc19ieV9mbGlnaHQkRmxpZ2h0ID09IGN1cnJlbnRfZmxpZ2h0LCAyOjVdIDwtCiAgICAgICAgYygKICAgICAgICAgICAgbWVhbihmbGlnaHRfZGF0YSRBc2NlbnRfUmF0ZV9tX3NbZmxpZ2h0X2RhdGEkRmxpZ2h0ID09IGN1cnJlbnRfZmxpZ2h0ICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZsaWdodF9kYXRhJEFzY2VudF9SYXRlX21fcyA+IDBdLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBtZWFuKGZsaWdodF9kYXRhJEFzY2VudF9SYXRlX21fc1tmbGlnaHRfZGF0YSRGbGlnaHQgPT0gY3VycmVudF9mbGlnaHQgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmxpZ2h0X2RhdGEkQXNjZW50X1JhdGVfbV9zIDwgMF0sIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIG1lYW4oZmxpZ2h0X2RhdGEkR3JvdW5kX1NwZWVkX21fc1tmbGlnaHRfZGF0YSRGbGlnaHQgPT0gY3VycmVudF9mbGlnaHRdLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBtZWFuKGZsaWdodF9kYXRhJEFzY2VudF9SYXRlX21fc1tmbGlnaHRfZGF0YSRBc2NlbnRfUmF0ZV9tX3MgPCAwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmxpZ2h0X2RhdGEkQWx0aXR1ZGVfbSA8IHNlYV9sZXZlbF9kZXNjZW50X3JhdGVfdGhyZXNob2xkICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmxpZ2h0X2RhdGEkR3JvdW5kX1NwZWVkX21fcyA+IDIgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmbGlnaHRfZGF0YSRGbGlnaHQgJWluJSBjdXJyZW50X2ZsaWdodF0sIG5hLnJtID0gVFJVRSkKICAgICAgICApCn0KCnJtKGN1cnJlbnRfZmxpZ2h0KQoKYXZlcmFnZXNfYnlfZmxpZ2h0W2lzLm5hKGF2ZXJhZ2VzX2J5X2ZsaWdodCldIDwtIE5BCgpybWFya2Rvd246OnBhZ2VkX3RhYmxlKGF2ZXJhZ2VzX2J5X2ZsaWdodCkKCmdncGxvdChtZWx0KGF2ZXJhZ2VzX2J5X2ZsaWdodCwKICAgICAgICAgICAgaWQudmFycyA9ICJGbGlnaHQiKSwKICAgICAgIGFlcygKICAgICAgICAgICB4ID0gRmxpZ2h0LAogICAgICAgICAgIHkgPSB2YWx1ZSwKICAgICAgICAgICBmaWxsID0gcmVwX2xlbigKICAgICAgICAgICAgICAgYXMuZmFjdG9yKGZsaWdodF90aW1lcyRCYWxsb29uX01hc3Nfa2dbZmxpZ2h0X3RpbWVzJEZsaWdodCAlaW4lIEZsaWdodF0pLAogICAgICAgICAgICAgICBsZW5ndGgub3V0ID0gbGVuZ3RoKEZsaWdodCkKICAgICAgICAgICApCiAgICAgICApKSArCiAgICBnZW9tX2NvbCgpICsKICAgIGZhY2V0X2dyaWQodmFyaWFibGUgfiAuLCBzY2FsZXMgPSAiZnJlZV95IikgKwogICAgbGFicyh0aXRsZSA9ICJBdmVyYWdlcyBieSBGbGlnaHQiLCBmaWxsID0gIkJhbGxvb24gTWFzcyAoa2cpIikgKwogICAgdGhlbWUoCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygxLjAzLCAxLjAzKSwKICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLAogICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gInJpZ2h0IiwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpCiAgICApCmBgYAoKYGBge3IgYXZlcmFnZV9ncm91bmRfc3BlZWRfdnNfdGltZV9vZl95ZWFyLCBmaWcuaGVpZ2h0PTcuNDE2NDA3OSwgZmlnLndpZHRoPTEyfQpnZ3Bsb3QoYXZlcmFnZXNfYnlfZmxpZ2h0LAogICAgICAgYWVzKAogICAgICAgICAgIHggPSBhcy5EYXRlKGZvcm1hdChmbGlnaHRfdGltZXMkRGF0ZSwgZm9ybWF0ID0gIiVtLSVkIiksIGZvcm1hdCA9ICIlbS0lZCIpLAogICAgICAgICAgIHkgPSBHcm91bmRfU3BlZWRfbV9zLAogICAgICAgICAgIGZpbGwgPSByZXBfbGVuKAogICAgICAgICAgICAgICBhcy5mYWN0b3IoZmxpZ2h0X3RpbWVzJEJhbGxvb25fTWFzc19rZ1tmbGlnaHRfdGltZXMkRmxpZ2h0ICVpbiUgRmxpZ2h0XSksCiAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSBsZW5ndGgoRmxpZ2h0KQogICAgICAgICAgICkKICAgICAgICkpICsKICAgIGdlb21fY29sKHdpZHRoID0gMiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIAogICAgZ2VvbV9wb2ludChzaXplID0gMC4xKSArCiAgICBzY2FsZV94X2RhdGUoCiAgICAgICAgZGF0ZV9icmVha3MgPSAiMSBtb250aCIsCiAgICAgICAgbGFiZWxzID0gZGF0ZV9mb3JtYXQoIiViIiksCiAgICAgICAgbGltaXRzID0gYXMuRGF0ZShjKCIwMS0wMSIsICIxMi0zMSIpLCBmb3JtYXQgPSAiJW0tJWQiKSwKICAgICAgICBleHBhbmQgPSBjKDAsIDApCiAgICApICsKICAgIGdlb21fZGwoYWVzKGxhYmVsID0gZ3N1YigiTlMiLCAiIiwgRmxpZ2h0KSksCiAgICAgICAgICAgIG1ldGhvZCA9IGxpc3QoY2V4ID0gMC44LCBkbC50cmFucyh5ID0geSArIDAuMiksICJ0b3AuYnVtcHVwIikpICsKICAgIGxhYnModGl0bGUgPSAiQXZlcmFnZSBHcm91bmQgU3BlZWQgdnMgVGltZSBvZiBZZWFyIiwKICAgICAgICAgeCA9ICJUaW1lIG9mIFllYXIiLAogICAgICAgICB5ID0gIkF2ZXJhZ2UgR3JvdW5kIFNwZWVkIChtL3MpIiwKICAgICAgICAgZmlsbCA9ICJCYWxsb29uIE1hc3MgKGtnKSIpICsKICAgIHRoZW1lKAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMSwgMS4wMyksCiAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwKICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJyaWdodCIKICAgICkKYGBgCgoKIyMgQXZlcmFnZXMgYnkgQmFsbG9vbiBNYXNzCgpgYGB7ciBhdmVyYWdlc19ieV9iYWxsb29uX21hc3MsIGZpZy5oZWlnaHQ9Ny40MTY0MDc5LCBmaWcud2lkdGg9MTJ9CmF2ZXJhZ2VzX2J5X2JhbGxvb25fbWFzcyA8LQogICAgZGF0YS5mcmFtZSgKICAgICAgICBCYWxsb29uX01hc3Nfa2cgPSB1bmlxdWUoZmxpZ2h0X3RpbWVzJEJhbGxvb25fTWFzc19rZyksCiAgICAgICAgQnVyc3RfQWx0aXR1ZGVfbSA9IDAsCiAgICAgICAgTGFuZGluZ19EaXN0YW5jZV9tID0gMCwKICAgICAgICBBc2NlbnRfUmF0ZV9tX3MgPSAwLAogICAgICAgIERlc2NlbnRfUmF0ZV9tX3MgPSAwLAogICAgICAgIEdyb3VuZF9TcGVlZF9tX3MgPSAwLAogICAgICAgIFNlYV9MZXZlbF9EZXNjZW50X1JhdGVfbV9zID0gMAogICAgKQoKCmF2ZXJhZ2VzX2J5X2JhbGxvb25fbWFzcyA8LQogICAgYXZlcmFnZXNfYnlfYmFsbG9vbl9tYXNzW2NvbXBsZXRlLmNhc2VzKGF2ZXJhZ2VzX2J5X2JhbGxvb25fbWFzcyRCYWxsb29uX01hc3Nfa2cpLF0KYXZlcmFnZXNfYnlfYmFsbG9vbl9tYXNzIDwtCiAgICBhdmVyYWdlc19ieV9iYWxsb29uX21hc3Nbb3JkZXIoYXZlcmFnZXNfYnlfYmFsbG9vbl9tYXNzJEJhbGxvb25fTWFzc19rZyksIF0Kcm93Lm5hbWVzKGF2ZXJhZ2VzX2J5X2JhbGxvb25fbWFzcykgPC0gTlVMTAoKZm9yIChjdXJyZW50X2JhbGxvb25fbWFzcyBpbiBhdmVyYWdlc19ieV9iYWxsb29uX21hc3MkQmFsbG9vbl9NYXNzX2tnKQp7CiAgICBjdXJyZW50X2ZsaWdodHMgPC0KICAgICAgICBmbGlnaHRfdGltZXNbIShmbGlnaHRfdGltZXMkRmxpZ2h0ICVpbiUgYXZlcmFnZV9leGNsdWRlZF9mbGlnaHRzKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICBmbGlnaHRfdGltZXMkQmFsbG9vbl9NYXNzX2tnID09IGN1cnJlbnRfYmFsbG9vbl9tYXNzICYKICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShmbGlnaHRfdGltZXMkQmFsbG9vbl9NYXNzX2tnKSwgIkZsaWdodCJdCiAgICAKICAgIGF2ZXJhZ2VzX2J5X2JhbGxvb25fbWFzc1thdmVyYWdlc19ieV9iYWxsb29uX21hc3MkQmFsbG9vbl9NYXNzX2tnID09IGN1cnJlbnRfYmFsbG9vbl9tYXNzLCAyOjddIDwtCiAgICAgICAgYygKICAgICAgICAgICAgbWVhbihtYXhpbXVtX3ZhbHVlc19ieV9mbGlnaHQkQnVyc3RfQWx0aXR1ZGVfbVttYXhpbXVtX3ZhbHVlc19ieV9mbGlnaHQkRmxpZ2h0ICVpbiUgY3VycmVudF9mbGlnaHRzXSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbWVhbihtYXhpbXVtX3ZhbHVlc19ieV9mbGlnaHQkTGFuZGluZ19EaXN0YW5jZV9tW21heGltdW1fdmFsdWVzX2J5X2ZsaWdodCRGbGlnaHQgJWluJSBjdXJyZW50X2ZsaWdodHNdLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBtZWFuKGF2ZXJhZ2VzX2J5X2ZsaWdodCRBc2NlbnRfUmF0ZV9tX3NbYXZlcmFnZXNfYnlfZmxpZ2h0JEZsaWdodCAlaW4lIGN1cnJlbnRfZmxpZ2h0c10sIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIG1lYW4oYXZlcmFnZXNfYnlfZmxpZ2h0JERlc2NlbnRfUmF0ZV9tX3NbYXZlcmFnZXNfYnlfZmxpZ2h0JEZsaWdodCAlaW4lIGN1cnJlbnRfZmxpZ2h0c10sIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIG1lYW4oYXZlcmFnZXNfYnlfZmxpZ2h0JEdyb3VuZF9TcGVlZF9tX3NbYXZlcmFnZXNfYnlfZmxpZ2h0JEZsaWdodCAlaW4lIGN1cnJlbnRfZmxpZ2h0c10sIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIG1lYW4oYXZlcmFnZXNfYnlfZmxpZ2h0JFNlYV9MZXZlbF9EZXNjZW50X1JhdGVfbV9zW2F2ZXJhZ2VzX2J5X2ZsaWdodCRGbGlnaHQgJWluJSBjdXJyZW50X2ZsaWdodHNdLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgKQp9CgpybShjdXJyZW50X2JhbGxvb25fbWFzcywgY3VycmVudF9mbGlnaHRzKQoKcm1hcmtkb3duOjpwYWdlZF90YWJsZShhdmVyYWdlc19ieV9iYWxsb29uX21hc3MpCgpnZ3Bsb3QoCiAgICBtZWx0KGF2ZXJhZ2VzX2J5X2JhbGxvb25fbWFzcywKICAgICAgICAgaWQudmFycyA9ICJCYWxsb29uX01hc3Nfa2ciKSwKICAgIGFlcygKICAgICAgICB4ID0gYXMuZmFjdG9yKEJhbGxvb25fTWFzc19rZyksCiAgICAgICAgeSA9IHZhbHVlLAogICAgICAgIGZpbGwgPSBhcy5mYWN0b3IoQmFsbG9vbl9NYXNzX2tnKQogICAgKQopICsKICAgIGdlb21fY29sKCkgKwogICAgZmFjZXRfd3JhcChmYWNldHMgPSAidmFyaWFibGUiLCBzY2FsZXMgPSAiZnJlZV95IikgKwogICAgbGFicyh0aXRsZSA9ICJBdmVyYWdlcyBieSBCYWxsb29uIE1hc3MiLCBmaWxsID0gIkJhbGxvb24gTWFzcyAoa2cpIikgKwogICAgdGhlbWUoCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygxLCAxLjA3KSwKICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLAogICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gInJpZ2h0IiwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpCiAgICApCmBgYApleGNsdWRpbmcgYHIgYXZlcmFnZV9leGNsdWRlZF9mbGlnaHRzYAoKIyBNYXNzIFJhdGlvcwoKYGBge3IgY2FsY3VsYXRlX21hc3NfcmF0aW9zfQptYXNzX3JhdGlvcyA8LSBmbGlnaHRfdGltZXNbYygxLCA3OjkpXQoKbWFzc19yYXRpb3MkQmFsbG9vbl9NYXNzX1JhdGlvIDwtCiAgICBtYXNzX3JhdGlvcyRCYWxsb29uX01hc3Nfa2cgLyBtYXNzX3JhdGlvcyRUb3RhbF9NYXNzX2tnCm1hc3NfcmF0aW9zJEhlbGl1bV9NYXNzX1JhdGlvIDwtCiAgICBtYXNzX3JhdGlvcyRIZWxpdW1fTWFzc19rZyAvIG1hc3NfcmF0aW9zJFRvdGFsX01hc3Nfa2cKbWFzc19yYXRpb3MkSGVsaXVtX0JhbGxvb25fUmF0aW8gPC0KICAgIG1hc3NfcmF0aW9zJEhlbGl1bV9NYXNzX2tnIC8gbWFzc19yYXRpb3MkQmFsbG9vbl9NYXNzX2tnCgpybWFya2Rvd246OnBhZ2VkX3RhYmxlKG1hc3NfcmF0aW9zKQpgYGAKCiMjIE1hc3MgdnMgRmxpZ2h0IFN0YXRpc3RpY3MKCmBgYHtyIG1hc3NfdnNfZmxpZ2h0X3N0YXRpc3RpY3MsIGZpZy5oZWlnaHQ9Ny40MTY0MDc5LCBmaWcud2lkdGg9MTJ9CmdncGxvdCgKICAgIG1lbHQoCiAgICAgICAgbWVsdCgKICAgICAgICAgICAgbWVyZ2UoYXZlcmFnZXNfYnlfZmxpZ2h0W2MoMSwgMiwgNSldLAogICAgICAgICAgICAgICAgICBtYXNzX3JhdGlvc1tjKDEsIDI6NCldLAogICAgICAgICAgICAgICAgICBieSA9IGMoIkZsaWdodCIpKSwKICAgICAgICAgICAgaWQudmFycyA9IGMoCiAgICAgICAgICAgICAgICAiRmxpZ2h0IiwKICAgICAgICAgICAgICAgICJCYWxsb29uX01hc3Nfa2ciLAogICAgICAgICAgICAgICAgIkFzY2VudF9SYXRlX21fcyIsCiAgICAgICAgICAgICAgICAiU2VhX0xldmVsX0Rlc2NlbnRfUmF0ZV9tX3MiCiAgICAgICAgICAgICksCiAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAibWFzc190eXBlIiwKICAgICAgICAgICAgdmFsdWUubmFtZSA9ICJtYXNzIgogICAgICAgICksCiAgICAgICAgaWQudmFycyA9IGMoIkZsaWdodCIsICJCYWxsb29uX01hc3Nfa2ciLCAibWFzc190eXBlIiwgIm1hc3MiKSwKICAgICAgICB2YXJpYWJsZS5uYW1lID0gInZhbHVlX3R5cGUiLAogICAgICAgIHZhbHVlLm5hbWUgPSAidmFsdWUiCiAgICApLAogICAgYWVzKAogICAgICAgIHggPSBtYXNzLAogICAgICAgIHkgPSB2YWx1ZSwKICAgICAgICBjb2xvdXIgPSBhcy5mYWN0b3IoQmFsbG9vbl9NYXNzX2tnKQogICAgKQopICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuMSkgKwogICAgZmFjZXRfZ3JpZCh2YWx1ZV90eXBlIH4gbWFzc190eXBlLCBzY2FsZXMgPSAiZnJlZSIpICsKICAgIGdlb21fZGwoYWVzKGxhYmVsID0gZ3N1YigiTlMiLCAiIiwgRmxpZ2h0KSksCiAgICAgICAgICAgIG1ldGhvZCA9IGxpc3QoY2V4ID0gMC44LCAidmlzdWFsY2VudGVyIiwgImJ1bXB1cCIpKSArCiAgICB0aGVtZSgKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDEuMDMsIDEuMDcpLAogICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsCiAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSAicmlnaHQiLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkKICAgICkgKwogICAgZ3VpZGVzKGNvbCA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNoYXBlID0gMTUsIHNpemUgPSA3KSkpICsKICAgIGxhYnModGl0bGUgPSAiTWFzcyB2cyBGbGlnaHQgU3RhdGlzdGljcyIsIGNvbG91ciA9ICJCYWxsb29uIE1hc3MgKGtnKSIpCmBgYAoKIyMgTWFzcyBSYXRpb3MgdnMgRmxpZ2h0IFN0YXRpc3RpY3MKCmBgYHtyIG1hc3NfcmF0aW9zX3ZzX2ZsaWdodF9zdGF0aXN0aWNzLCBmaWcuaGVpZ2h0PTcuNDE2NDA3OSwgZmlnLndpZHRoPTEyfQpnZ3Bsb3QoCiAgICBtZWx0KAogICAgICAgIG1lbHQoCiAgICAgICAgICAgIG1lcmdlKAogICAgICAgICAgICAgICAgbWVyZ2UoYXZlcmFnZXNfYnlfZmxpZ2h0W2MoMSwgMildLAogICAgICAgICAgICAgICAgICAgICAgbWFzc19yYXRpb3NbYygxOjQpXSwKICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gIkZsaWdodCIpLAogICAgICAgICAgICAgICAgbWF4aW11bV92YWx1ZXNfYnlfZmxpZ2h0W2MoMSwgMildLAogICAgICAgICAgICAgICAgYnkgPSAiRmxpZ2h0IgogICAgICAgICAgICApLAogICAgICAgICAgICBpZC52YXJzID0gYygKICAgICAgICAgICAgICAgICJGbGlnaHQiLAogICAgICAgICAgICAgICAgIkJhbGxvb25fTWFzc19rZyIsCiAgICAgICAgICAgICAgICAiQXNjZW50X1JhdGVfbV9zIiwKICAgICAgICAgICAgICAgICJCdXJzdF9BbHRpdHVkZV9tIgogICAgICAgICAgICApLAogICAgICAgICAgICB2YXJpYWJsZS5uYW1lID0gInJhdGlvX3R5cGUiLAogICAgICAgICAgICB2YWx1ZS5uYW1lID0gInJhdGlvIgogICAgICAgICksCiAgICAgICAgaWQudmFycyA9IGMoIkZsaWdodCIsICJCYWxsb29uX01hc3Nfa2ciLCAicmF0aW9fdHlwZSIsICJyYXRpbyIpLAogICAgICAgIHZhcmlhYmxlLm5hbWUgPSAidmFsdWVfdHlwZSIsCiAgICAgICAgdmFsdWUubmFtZSA9ICJ2YWx1ZSIKICAgICksCiAgICBhZXMoCiAgICAgICAgeCA9IHJhdGlvLAogICAgICAgIHkgPSB2YWx1ZSwKICAgICAgICBjb2xvdXIgPSBhcy5mYWN0b3IoQmFsbG9vbl9NYXNzX2tnKQogICAgKQopICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuMSkgKwogICAgZmFjZXRfZ3JpZCh2YWx1ZV90eXBlIH4gcmF0aW9fdHlwZSwgc2NhbGVzID0gImZyZWUiKSArCiAgICBnZW9tX2RsKGFlcyhsYWJlbCA9IGdzdWIoIk5TIiwgIiIsIEZsaWdodCkpLAogICAgICAgICAgICBtZXRob2QgPSBsaXN0KGNleCA9IDAuOCwgInZpc3VhbGNlbnRlciIsICJidW1wdXAiKSkgKwogICAgdGhlbWUoCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygxLjAzLCAxLjA3KSwKICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLAogICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gInJpZ2h0IiwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpCiAgICApICsKICAgIGd1aWRlcyhjb2wgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaGFwZSA9IDE1LCBzaXplID0gNykpKSArCiAgICBsYWJzKHRpdGxlID0gIk1hc3MgUmF0aW9zIHZzIEZsaWdodCBTdGF0aXN0aWNzIiwgY29sb3VyID0gIkJhbGxvb24gTWFzcyAoa2cpIikKYGBgCgpgYGB7ciB0ZWFyZG93biwgaW5jbHVkZT1GQUxTRX0Kcm0ocmVhZF9mbGlnaHRfdGltZXMsIHJlYWRfZmxpZ2h0X2RhdGEpCgpybShzbWFsbC5ib3gubGFiZWxzKQojcm0oZmxpZ2h0X3RpbWVzKQojcm0oZmxpZ2h0X2RhdGEpCgojcm0oYXZlcmFnZXNfYnlfYmFsbG9vbl9tYXNzLCBhdmVyYWdlc19ieV9mbGlnaHQsIG1heGltdW1fdmFsdWVzX2J5X2ZsaWdodCwgbWFzc19yYXRpb3MsIG92ZXJhbGxfYXZlcmFnZXMsIG92ZXJhbGxfcmVjb3JkcykKYGBgCgo=