Marine Biodiversity Observation Network Pole to Pole of the Americas (MBON Pole to Pole)
Written by E. Montes (emontesh@usf.edu) and Eduardo Klein (eklein@usb.ve) on Auguts 28, 2020.
This code pulls data from NOAA’s ERDDAP servers and creates time series plots of sea surface temperature (SST) and chlorophyll-a concentration (CHL), and maps showing the latest available data for the selected region.
Step 1
First, let’s load required libraries
library(readr)
library(rerddap)
library(lubridate)
library(dplyr)
library(flexdashboard)
library(reshape2)
library(leaflet)
library(ggplot2)
library(vegan)
library(xts)
library(dygraphs)
library(plotly)
library(mapdata)
library(RColorBrewer)
palette(brewer.pal(8, "Set2"))
Step 2
Query SST data from ERDDAP
## remove all spaces from string
NoSpaces = function(x){
return(gsub(" ", "", x))
}
## set site coordinates and time for SST extraction
SSTSiteName = "Patagonia" ## for the resulting file name
SSTcoords.lon = -63.
SSTcoords.lat = -42.5
SSTstartDate = "2002-06-01"
## set climatological date start-end
SSTclimStartDate = "2002-06-01"
SSTclimEndDate = "2012-12-31"
## set dataset source
SSTsource = info("jplMURSST41")
##
## Get sst
SST <- griddap(SSTsource,
time=c(SSTstartDate, "last"),
longitude = c(SSTcoords.lon,SSTcoords.lon),
latitude = c(SSTcoords.lat,SSTcoords.lat),
fields = "analysed_sst",
fmt = "csv")
SST = SST[,c(1,4)]
names(SST) = c("time", "SST")
## convert time to a Data object
SST$time = as.Date(ymd_hms(SST$time))
Step 3
Calculate SST climatology
SST.clim = SST %>% filter(time>=ymd(SSTclimStartDate), time<=SSTclimEndDate) %>%
group_by(yDay = yday(time)) %>%
summarise(SST.mean = mean(SST),
SST.median = median(SST),
SST.sd = sd(SST),
SST.q5 = quantile(SST, 0.05),
SST.q10 = quantile(SST, 0.10),
SST.q25 = quantile(SST, 0.25),
SST.q75 = quantile(SST, 0.75),
SST.q90 = quantile(SST, 0.90),
SST.q95 = quantile(SST, 0.95),
SST.min = min(SST),
SST.max = max(SST))
Step 4
Plot SST time series
SST.xts = as.xts(SST$SST, SST$time)
dygraph(SST.xts,
ylab = "Sea Surface Temperature (Deg C)") %>%
dySeries("V1", label ="SST (Deg C)", color = "steelblue") %>%
dyHighlight(highlightCircleSize = 5,
highlightSeriesBackgroundAlpha = 0.2,
hideOnMouseOut = FALSE) %>%
dyOptions(fillGraph = FALSE, fillAlpha = 0.4) %>%
dyRangeSelector(dateWindow = c(max(SST$time) - years(5), max(SST$time)))
## subset SST for last year
SST.lastyear = SST %>% filter(year(time)==max(year(time)))
## make the plot
pp = ggplot(SST.clim, aes(yDay, SST.mean))
pp = pp + geom_line() + geom_smooth(span=0.25, se=FALSE, colour="steelblue") +
geom_ribbon(aes(ymin=SST.q25, ymax=SST.q75), fill="steelblue", alpha=0.5) +
geom_line(data=SST.lastyear, aes(yday(time), SST), colour="red") +
ylab("Sea Surface Temperature (Deg C)") + xlab("Day of the Year") +
theme_bw(base_size = 9)
ggplotly(pp) %>% plotly::config(displayModeBar = F)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'
Step 5
Save SST time series data
write_csv(SST, path = paste0(NoSpaces(SSTSiteName), "_SST.csv"))
write_csv(SST.clim, path = paste0(NoSpaces(SSTSiteName), "_Climatology.csv"))
Step 6
Create a map of the latest SST data
sstInfo <- info('jplMURSST41')
# get latest 3-day composite sst
GHRSST <- griddap(sstInfo, latitude = c(-60., -20.), longitude = c(-90., -47.), time = c('last','last'), fields = 'analysed_sst')
mycolor <- colors$temperature
w <- map_data("worldHires", ylim = c(-60., -20.), xlim = c(-90., -47.))
ggplot(data = GHRSST$data, aes(x = lon, y = lat, fill = analysed_sst)) +
geom_polygon(data = w, aes(x = long, y = lat, group = group), fill = "grey80") +
geom_raster(interpolate = FALSE) +
scale_fill_gradientn(colours = mycolor, na.value = NA) +
theme_bw() + ylab("latitude") + xlab("longitude") +
coord_fixed(1.3, xlim = c(-90., -47.), ylim = c(-60., -20.)) + ggtitle("Latest daily SST data")

Step 7
Query CHL data from ERDDAP
## remove all spaces from string
NoSpaces = function(x){
return(gsub(" ", "", x))
}
## set site coordinates and time for CHL extraction
CHLSiteName = "Patagonia" ## for the resulting file name
CHLcoords.lon = -63
CHLcoords.lat = 42.5
CHLstartDate = "2012-01-01"
## set climatological date start-end
CHLclimStartDate = "2012-01-01"
CHLclimEndDate = "2016-12-31"
## set dataset source
CHLsource = info("erdMH1chla8day")
##
## Get CHL
CHL <- griddap(CHLsource,
time=c(CHLstartDate, "last"),
longitude = c(CHLcoords.lon,CHLcoords.lon),
latitude = c(CHLcoords.lat,CHLcoords.lat),
fields = "chlorophyll", fmt = "csv")
CHL = CHL[,c(1,4)]
names(CHL) = c("time", "CHL")
CHL = na.omit(CHL)
## convert time to a Data object
CHL$time = as.Date(ymd_hms(CHL$time))
Step 8
Calculate CHL climatology
CHL.clim = CHL %>% filter(time>=ymd(CHLclimStartDate), time<=CHLclimEndDate) %>%
group_by(yDay = yday(time)) %>%
summarise(CHL.mean = mean(CHL),
CHL.median = median(CHL),
CHL.sd = sd(CHL),
CHL.q5 = quantile(CHL, 0.05),
CHL.q10 = quantile(CHL, 0.10),
CHL.q25 = quantile(CHL, 0.25),
CHL.q75 = quantile(CHL, 0.75),
CHL.q90 = quantile(CHL, 0.90),
CHL.q95 = quantile(CHL, 0.95),
CHL.min = min(CHL),
CHL.max = max(CHL))
Step 9
Plot CHL time series
CHL.xts = as.xts(CHL$CHL, CHL$time)
dygraph(CHL.xts,
ylab = "Chlorophyll a (mg m-3)") %>%
dySeries("V1", label ="CHL", color = "steelblue") %>%
dyHighlight(highlightCircleSize = 5,
highlightSeriesBackgroundAlpha = 0.2,
hideOnMouseOut = FALSE) %>%
dyOptions(fillGraph = FALSE, fillAlpha = 0.4) %>%
dyRangeSelector(dateWindow = c(max(CHL$time) - years(5), max(CHL$time)))
### CHL Last year with smoothed Climatology {data-width=250}
## subset CHL for last year
CHL.lastyear = CHL %>% filter(year(time)==max(year(time)))
## make the plot
pp = ggplot(CHL.clim, aes(yDay, CHL.mean))
pp = pp + geom_line() + geom_smooth(span=0.25, se=FALSE, colour="steelblue") +
geom_ribbon(aes(ymin=CHL.q25, ymax=CHL.q75), fill="steelblue", alpha=0.5) +
geom_line(data=CHL.lastyear, aes(yday(time), CHL), colour="red") +
ylab("Chlorophyll a (mg m-3)") + xlab("Day of the Year") +
theme_bw(base_size = 9)
ggplotly(pp) %>% plotly::config(displayModeBar = F)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'
#Step 10 Save CHL time series data
write_csv(CHL, path = paste0(NoSpaces(CHLSiteName), "_CHL.csv"))
write_csv(CHL.clim, path = paste0(NoSpaces(CHLSiteName), "_Climatology.csv"))
#Step 11 Create a map of the latest CHL data
require("rerddap")
require("ggplot2")
require("mapdata")
# get latest Monthly chl (VIIRS)
chlaInfo <- info('nesdisVHNSQchlaMonthly')
viirsCHLA <- griddap(chlaInfo, latitude = c(-20., -60.), longitude = c(-90., -47.), time = c('last','last'), fields = 'chlor_a')
# get latest 8-day chl (MODIS)
chlaInfo_8d <- info('erdMH1chla8day')
MODIS_CHLA_8d <- griddap(chlaInfo_8d, latitude = c(-20., -60.), longitude = c(-90., -47.), time = c('last','last'), fields = 'chlorophyll')
# Map monthly chl (VIIRS)
mycolor <- colors$chlorophyll
w <- map_data("worldHires", ylim = c(-60., -20.), xlim = c(-90., -47.))
ggplot(data = viirsCHLA$data, aes(x = lon, y = lat, fill = log(chlor_a))) +
geom_polygon(data = w, aes(x = long, y = lat, group = group), fill = "grey80") +
geom_raster(interpolate = FALSE) +
scale_fill_gradientn(colours = mycolor, na.value = NA) +
theme_bw() + ylab("latitude") + xlab("longitude") +
coord_fixed(1.3, xlim = c(-90., -47.), ylim = c(-60., -20.)) + ggtitle("Latest VIIRS Monthly Chla")

# Map 8-day chl (MODIS)
mycolor <- colors$chlorophyll
w <- map_data("worldHires", ylim = c(-60., -20.), xlim = c(-90., -47.))
ggplot(data = MODIS_CHLA_8d$data, aes(x = lon, y = lat, fill = log(chlorophyll))) +
geom_polygon(data = w, aes(x = long, y = lat, group = group), fill = "grey80") +
geom_raster(interpolate = FALSE) +
scale_fill_gradientn(colours = mycolor, na.value = NA) +
theme_bw() + ylab("latitude") + xlab("longitude") +
coord_fixed(1.3, xlim = c(-90., -47.), ylim = c(-60., -20.)) + ggtitle("Latest MODIS 8-day Chla")

LS0tCnRpdGxlOiAiU2F0ZWxsaXRlIFNTVCBhbmQgQ0hMIGRhdGEgZXh0cmFjdGlvbnMgZnJvbSBzZWxlY3RlZCBsb2NhdGlvbnMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIE1hcmluZSBCaW9kaXZlcnNpdHkgT2JzZXJ2YXRpb24gTmV0d29yayBQb2xlIHRvIFBvbGUgb2YgdGhlIEFtZXJpY2FzIChNQk9OIFBvbGUgdG8gUG9sZSkKCldyaXR0ZW4gYnkgRS4gTW9udGVzIChlbW9udGVzaEB1c2YuZWR1KSBhbmQgRWR1YXJkbyBLbGVpbiAoZWtsZWluQHVzYi52ZSkgb24gQXVndXRzIDI4LCAyMDIwLgoKVGhpcyBjb2RlIHB1bGxzIGRhdGEgZnJvbSBOT0FBJ3MgW0VSRERBUF0oaHR0cHM6Ly9jb2FzdHdhdGNoLnBmZWcubm9hYS5nb3YvZXJkZGFwL2luZGV4Lmh0bWwpIHNlcnZlcnMgYW5kIGNyZWF0ZXMgdGltZSBzZXJpZXMgcGxvdHMgb2Ygc2VhIHN1cmZhY2UgdGVtcGVyYXR1cmUgKFNTVCkgYW5kIGNobG9yb3BoeWxsLWEgY29uY2VudHJhdGlvbiAoQ0hMKSwgYW5kIG1hcHMgc2hvd2luZyB0aGUgbGF0ZXN0IGF2YWlsYWJsZSBkYXRhIGZvciB0aGUgc2VsZWN0ZWQgcmVnaW9uLgoKIyBTdGVwIDEKRmlyc3QsIGxldCdzIGxvYWQgcmVxdWlyZWQgbGlicmFyaWVzCmBgYHtyfQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHJlcmRkYXApCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGZsZXhkYXNoYm9hcmQpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHZlZ2FuKQpsaWJyYXJ5KHh0cykKbGlicmFyeShkeWdyYXBocykKbGlicmFyeShwbG90bHkpCmxpYnJhcnkobWFwZGF0YSkKCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpwYWxldHRlKGJyZXdlci5wYWwoOCwgIlNldDIiKSkKYGBgCgojIFN0ZXAgMgpRdWVyeSBTU1QgZGF0YSBmcm9tIEVSRERBUApgYGB7cn0KIyMgcmVtb3ZlIGFsbCBzcGFjZXMgZnJvbSBzdHJpbmcKTm9TcGFjZXMgPSBmdW5jdGlvbih4KXsKICByZXR1cm4oZ3N1YigiICIsICIiLCB4KSkKfQoKIyMgc2V0IHNpdGUgY29vcmRpbmF0ZXMgYW5kIHRpbWUgZm9yIFNTVCBleHRyYWN0aW9uClNTVFNpdGVOYW1lID0gIlBhdGFnb25pYSIgICAjIyBmb3IgdGhlIHJlc3VsdGluZyBmaWxlIG5hbWUKU1NUY29vcmRzLmxvbiA9IC02My4KU1NUY29vcmRzLmxhdCA9IC00Mi41CgpTU1RzdGFydERhdGUgPSAiMjAwMi0wNi0wMSIKCiMjIHNldCBjbGltYXRvbG9naWNhbCBkYXRlIHN0YXJ0LWVuZApTU1RjbGltU3RhcnREYXRlID0gIjIwMDItMDYtMDEiClNTVGNsaW1FbmREYXRlID0gIjIwMTItMTItMzEiCgojIyBzZXQgZGF0YXNldCBzb3VyY2UKU1NUc291cmNlID0gaW5mbygianBsTVVSU1NUNDEiKQoKIyMKIyMgR2V0IHNzdCAKU1NUIDwtIGdyaWRkYXAoU1NUc291cmNlLCAKICAgICAgICAgICAgICB0aW1lPWMoU1NUc3RhcnREYXRlLCAibGFzdCIpLAogICAgICAgICAgICAgIGxvbmdpdHVkZSA9IGMoU1NUY29vcmRzLmxvbixTU1Rjb29yZHMubG9uKSwKICAgICAgICAgICAgICBsYXRpdHVkZSA9IGMoU1NUY29vcmRzLmxhdCxTU1Rjb29yZHMubGF0KSwKICAgICAgICAgICAgICBmaWVsZHMgPSAiYW5hbHlzZWRfc3N0IiwKICAgICAgICAgICAgICBmbXQgPSAiY3N2IikKClNTVCA9IFNTVFssYygxLDQpXQpuYW1lcyhTU1QpID0gYygidGltZSIsICJTU1QiKQoKIyMgY29udmVydCB0aW1lIHRvIGEgRGF0YSBvYmplY3QKU1NUJHRpbWUgPSBhcy5EYXRlKHltZF9obXMoU1NUJHRpbWUpKQoKYGBgCgojIFN0ZXAgMwpDYWxjdWxhdGUgU1NUIGNsaW1hdG9sb2d5CmBgYHtyfQpTU1QuY2xpbSA9IFNTVCAlPiUgZmlsdGVyKHRpbWU+PXltZChTU1RjbGltU3RhcnREYXRlKSwgdGltZTw9U1NUY2xpbUVuZERhdGUpICU+JSAKICBncm91cF9ieSh5RGF5ID0geWRheSh0aW1lKSkgJT4lIAogIHN1bW1hcmlzZShTU1QubWVhbiA9IG1lYW4oU1NUKSwKICAgICAgICAgICAgU1NULm1lZGlhbiA9IG1lZGlhbihTU1QpLAogICAgICAgICAgICBTU1Quc2QgPSBzZChTU1QpLAogICAgICAgICAgICBTU1QucTUgPSBxdWFudGlsZShTU1QsIDAuMDUpLAogICAgICAgICAgICBTU1QucTEwID0gcXVhbnRpbGUoU1NULCAwLjEwKSwKICAgICAgICAgICAgU1NULnEyNSA9IHF1YW50aWxlKFNTVCwgMC4yNSksCiAgICAgICAgICAgIFNTVC5xNzUgPSBxdWFudGlsZShTU1QsIDAuNzUpLAogICAgICAgICAgICBTU1QucTkwID0gcXVhbnRpbGUoU1NULCAwLjkwKSwKICAgICAgICAgICAgU1NULnE5NSA9IHF1YW50aWxlKFNTVCwgMC45NSksCiAgICAgICAgICAgIFNTVC5taW4gPSBtaW4oU1NUKSwKICAgICAgICAgICAgU1NULm1heCA9IG1heChTU1QpKQpgYGAKCiMgU3RlcCA0ClBsb3QgU1NUIHRpbWUgc2VyaWVzCmBgYHtyfQpTU1QueHRzID0gYXMueHRzKFNTVCRTU1QsIFNTVCR0aW1lKQpkeWdyYXBoKFNTVC54dHMsIAogICAgICAgIHlsYWIgPSAiU2VhIFN1cmZhY2UgVGVtcGVyYXR1cmUgKERlZyBDKSIpICU+JSAKICBkeVNlcmllcygiVjEiLCBsYWJlbCA9IlNTVCAoRGVnIEMpIiwgY29sb3IgPSAic3RlZWxibHVlIikgJT4lCiAgZHlIaWdobGlnaHQoaGlnaGxpZ2h0Q2lyY2xlU2l6ZSA9IDUsIAogICAgICAgICAgICAgIGhpZ2hsaWdodFNlcmllc0JhY2tncm91bmRBbHBoYSA9IDAuMiwKICAgICAgICAgICAgICBoaWRlT25Nb3VzZU91dCA9IEZBTFNFKSAlPiUgCiAgZHlPcHRpb25zKGZpbGxHcmFwaCA9IEZBTFNFLCBmaWxsQWxwaGEgPSAwLjQpICU+JSAKICBkeVJhbmdlU2VsZWN0b3IoZGF0ZVdpbmRvdyA9IGMobWF4KFNTVCR0aW1lKSAtIHllYXJzKDUpLCBtYXgoU1NUJHRpbWUpKSkKCiMjIHN1YnNldCBTU1QgZm9yIGxhc3QgeWVhcgpTU1QubGFzdHllYXIgPSBTU1QgJT4lIGZpbHRlcih5ZWFyKHRpbWUpPT1tYXgoeWVhcih0aW1lKSkpCgojIyBtYWtlIHRoZSBwbG90CnBwID0gZ2dwbG90KFNTVC5jbGltLCBhZXMoeURheSwgU1NULm1lYW4pKQpwcCA9IHBwICsgZ2VvbV9saW5lKCkgKyBnZW9tX3Ntb290aChzcGFuPTAuMjUsIHNlPUZBTFNFLCBjb2xvdXI9InN0ZWVsYmx1ZSIpICsgIAogIGdlb21fcmliYm9uKGFlcyh5bWluPVNTVC5xMjUsIHltYXg9U1NULnE3NSksIGZpbGw9InN0ZWVsYmx1ZSIsIGFscGhhPTAuNSkgKwogIGdlb21fbGluZShkYXRhPVNTVC5sYXN0eWVhciwgYWVzKHlkYXkodGltZSksIFNTVCksIGNvbG91cj0icmVkIikgKyAKICB5bGFiKCJTZWEgU3VyZmFjZSBUZW1wZXJhdHVyZSAoRGVnIEMpIikgKyB4bGFiKCJEYXkgb2YgdGhlIFllYXIiKSArIAogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDkpIApnZ3Bsb3RseShwcCkgJT4lIHBsb3RseTo6Y29uZmlnKGRpc3BsYXlNb2RlQmFyID0gRikgCmBgYAoKIyBTdGVwIDUKU2F2ZSBTU1QgdGltZSBzZXJpZXMgZGF0YQpgYGB7cn0Kd3JpdGVfY3N2KFNTVCwgcGF0aCA9IHBhc3RlMChOb1NwYWNlcyhTU1RTaXRlTmFtZSksICJfU1NULmNzdiIpKQp3cml0ZV9jc3YoU1NULmNsaW0sIHBhdGggPSBwYXN0ZTAoTm9TcGFjZXMoU1NUU2l0ZU5hbWUpLCAiX0NsaW1hdG9sb2d5LmNzdiIpKQpgYGAKCiMgU3RlcCA2CkNyZWF0ZSBhIG1hcCBvZiB0aGUgbGF0ZXN0IFNTVCBkYXRhCmBgYHtyfQpzc3RJbmZvIDwtIGluZm8oJ2pwbE1VUlNTVDQxJykKIyBnZXQgbGF0ZXN0IDMtZGF5IGNvbXBvc2l0ZSBzc3QKR0hSU1NUIDwtIGdyaWRkYXAoc3N0SW5mbywgbGF0aXR1ZGUgPSBjKC02MC4sIC0yMC4pLCBsb25naXR1ZGUgPSBjKC05MC4sIC00Ny4pLCB0aW1lID0gYygnbGFzdCcsJ2xhc3QnKSwgZmllbGRzID0gJ2FuYWx5c2VkX3NzdCcpCgpteWNvbG9yIDwtIGNvbG9ycyR0ZW1wZXJhdHVyZQp3IDwtIG1hcF9kYXRhKCJ3b3JsZEhpcmVzIiwgeWxpbSA9IGMoLTYwLiwgLTIwLiksIHhsaW0gPSBjKC05MC4sIC00Ny4pKQpnZ3Bsb3QoZGF0YSA9IEdIUlNTVCRkYXRhLCBhZXMoeCA9IGxvbiwgeSA9IGxhdCwgZmlsbCA9IGFuYWx5c2VkX3NzdCkpICsgCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSB3LCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCBmaWxsID0gImdyZXk4MCIpICsKICBnZW9tX3Jhc3RlcihpbnRlcnBvbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IG15Y29sb3IsIG5hLnZhbHVlID0gTkEpICsKICB0aGVtZV9idygpICsgeWxhYigibGF0aXR1ZGUiKSArIHhsYWIoImxvbmdpdHVkZSIpICsKICBjb29yZF9maXhlZCgxLjMsIHhsaW0gPSBjKC05MC4sIC00Ny4pLCAgeWxpbSA9IGMoLTYwLiwgLTIwLikpICsgZ2d0aXRsZSgiTGF0ZXN0IGRhaWx5IFNTVCBkYXRhIikKYGBgCgoKIyBTdGVwIDcKUXVlcnkgQ0hMIGRhdGEgZnJvbSBFUkREQVAKYGBge3J9CiMjIHJlbW92ZSBhbGwgc3BhY2VzIGZyb20gc3RyaW5nCk5vU3BhY2VzID0gZnVuY3Rpb24oeCl7CiAgcmV0dXJuKGdzdWIoIiAiLCAiIiwgeCkpCn0KCiMjIHNldCBzaXRlIGNvb3JkaW5hdGVzIGFuZCB0aW1lIGZvciBDSEwgZXh0cmFjdGlvbgpDSExTaXRlTmFtZSA9ICJQYXRhZ29uaWEiICAgIyMgZm9yIHRoZSByZXN1bHRpbmcgZmlsZSBuYW1lCkNITGNvb3Jkcy5sb24gPSAtNjMKQ0hMY29vcmRzLmxhdCA9IC00Mi41CgpDSExzdGFydERhdGUgPSAiMjAxMi0wMS0wMSIKCiMjIHNldCBjbGltYXRvbG9naWNhbCBkYXRlIHN0YXJ0LWVuZApDSExjbGltU3RhcnREYXRlID0gIjIwMTItMDEtMDEiCkNITGNsaW1FbmREYXRlID0gIjIwMTYtMTItMzEiCgojIyBzZXQgZGF0YXNldCBzb3VyY2UKQ0hMc291cmNlID0gaW5mbygiZXJkTUgxY2hsYThkYXkiKQoKIyMKIyMgR2V0IENITCAKQ0hMIDwtIGdyaWRkYXAoQ0hMc291cmNlLCAKICAgICAgICAgICAgICAgdGltZT1jKENITHN0YXJ0RGF0ZSwgImxhc3QiKSwKICAgICAgICAgICAgICAgbG9uZ2l0dWRlID0gYyhDSExjb29yZHMubG9uLENITGNvb3Jkcy5sb24pLAogICAgICAgICAgICAgICBsYXRpdHVkZSA9IGMoQ0hMY29vcmRzLmxhdCxDSExjb29yZHMubGF0KSwKICAgICAgICAgICAgICAgZmllbGRzID0gImNobG9yb3BoeWxsIiwgZm10ID0gImNzdiIpCgpDSEwgPSBDSExbLGMoMSw0KV0KbmFtZXMoQ0hMKSA9IGMoInRpbWUiLCAiQ0hMIikKQ0hMID0gbmEub21pdChDSEwpCgojIyBjb252ZXJ0IHRpbWUgdG8gYSBEYXRhIG9iamVjdApDSEwkdGltZSA9IGFzLkRhdGUoeW1kX2htcyhDSEwkdGltZSkpCgpgYGAKCiMgU3RlcCA4CkNhbGN1bGF0ZSBDSEwgY2xpbWF0b2xvZ3kKYGBge3J9CkNITC5jbGltID0gQ0hMICU+JSBmaWx0ZXIodGltZT49eW1kKENITGNsaW1TdGFydERhdGUpLCB0aW1lPD1DSExjbGltRW5kRGF0ZSkgJT4lIAogIGdyb3VwX2J5KHlEYXkgPSB5ZGF5KHRpbWUpKSAlPiUgCiAgc3VtbWFyaXNlKENITC5tZWFuID0gbWVhbihDSEwpLAogICAgICAgICAgICBDSEwubWVkaWFuID0gbWVkaWFuKENITCksCiAgICAgICAgICAgIENITC5zZCA9IHNkKENITCksCiAgICAgICAgICAgIENITC5xNSA9IHF1YW50aWxlKENITCwgMC4wNSksCiAgICAgICAgICAgIENITC5xMTAgPSBxdWFudGlsZShDSEwsIDAuMTApLAogICAgICAgICAgICBDSEwucTI1ID0gcXVhbnRpbGUoQ0hMLCAwLjI1KSwKICAgICAgICAgICAgQ0hMLnE3NSA9IHF1YW50aWxlKENITCwgMC43NSksCiAgICAgICAgICAgIENITC5xOTAgPSBxdWFudGlsZShDSEwsIDAuOTApLAogICAgICAgICAgICBDSEwucTk1ID0gcXVhbnRpbGUoQ0hMLCAwLjk1KSwKICAgICAgICAgICAgQ0hMLm1pbiA9IG1pbihDSEwpLAogICAgICAgICAgICBDSEwubWF4ID0gbWF4KENITCkpCmBgYAoKCiMgU3RlcCA5ClBsb3QgQ0hMIHRpbWUgc2VyaWVzCmBgYHtyfQpDSEwueHRzID0gYXMueHRzKENITCRDSEwsIENITCR0aW1lKQpkeWdyYXBoKENITC54dHMsIAogICAgICAgIHlsYWIgPSAiQ2hsb3JvcGh5bGwgYSAobWcgbS0zKSIpICU+JSAKICBkeVNlcmllcygiVjEiLCBsYWJlbCA9IkNITCIsIGNvbG9yID0gInN0ZWVsYmx1ZSIpICU+JQogIGR5SGlnaGxpZ2h0KGhpZ2hsaWdodENpcmNsZVNpemUgPSA1LCAKICAgICAgICAgICAgICBoaWdobGlnaHRTZXJpZXNCYWNrZ3JvdW5kQWxwaGEgPSAwLjIsCiAgICAgICAgICAgICAgaGlkZU9uTW91c2VPdXQgPSBGQUxTRSkgJT4lIAogIGR5T3B0aW9ucyhmaWxsR3JhcGggPSBGQUxTRSwgZmlsbEFscGhhID0gMC40KSAlPiUgCiAgZHlSYW5nZVNlbGVjdG9yKGRhdGVXaW5kb3cgPSBjKG1heChDSEwkdGltZSkgLSB5ZWFycyg1KSwgbWF4KENITCR0aW1lKSkpCgojIyMgQ0hMIExhc3QgeWVhciB3aXRoIHNtb290aGVkIENsaW1hdG9sb2d5IHtkYXRhLXdpZHRoPTI1MH0KCiMjIHN1YnNldCBDSEwgZm9yIGxhc3QgeWVhcgpDSEwubGFzdHllYXIgPSBDSEwgJT4lIGZpbHRlcih5ZWFyKHRpbWUpPT1tYXgoeWVhcih0aW1lKSkpCgojIyBtYWtlIHRoZSBwbG90CnBwID0gZ2dwbG90KENITC5jbGltLCBhZXMoeURheSwgQ0hMLm1lYW4pKQpwcCA9IHBwICsgZ2VvbV9saW5lKCkgKyBnZW9tX3Ntb290aChzcGFuPTAuMjUsIHNlPUZBTFNFLCBjb2xvdXI9InN0ZWVsYmx1ZSIpICsgIAogIGdlb21fcmliYm9uKGFlcyh5bWluPUNITC5xMjUsIHltYXg9Q0hMLnE3NSksIGZpbGw9InN0ZWVsYmx1ZSIsIGFscGhhPTAuNSkgKwogIGdlb21fbGluZShkYXRhPUNITC5sYXN0eWVhciwgYWVzKHlkYXkodGltZSksIENITCksIGNvbG91cj0icmVkIikgKyAKICB5bGFiKCJDaGxvcm9waHlsbCBhIChtZyBtLTMpIikgKyB4bGFiKCJEYXkgb2YgdGhlIFllYXIiKSArIAogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDkpIApnZ3Bsb3RseShwcCkgJT4lIHBsb3RseTo6Y29uZmlnKGRpc3BsYXlNb2RlQmFyID0gRikgCgpgYGAKCiNTdGVwIDEwClNhdmUgQ0hMIHRpbWUgc2VyaWVzIGRhdGEKYGBge3J9CndyaXRlX2NzdihDSEwsIHBhdGggPSBwYXN0ZTAoTm9TcGFjZXMoQ0hMU2l0ZU5hbWUpLCAiX0NITC5jc3YiKSkKd3JpdGVfY3N2KENITC5jbGltLCBwYXRoID0gcGFzdGUwKE5vU3BhY2VzKENITFNpdGVOYW1lKSwgIl9DbGltYXRvbG9neS5jc3YiKSkKYGBgCgojU3RlcCAxMQpDcmVhdGUgYSBtYXAgb2YgdGhlIGxhdGVzdCBDSEwgZGF0YQpgYGB7cn0KcmVxdWlyZSgicmVyZGRhcCIpCnJlcXVpcmUoImdncGxvdDIiKQpyZXF1aXJlKCJtYXBkYXRhIikKCiMgZ2V0IGxhdGVzdCBNb250aGx5IGNobCAoVklJUlMpCmNobGFJbmZvIDwtIGluZm8oJ25lc2Rpc1ZITlNRY2hsYU1vbnRobHknKQp2aWlyc0NITEEgPC0gZ3JpZGRhcChjaGxhSW5mbywgbGF0aXR1ZGUgPSBjKC0yMC4sIC02MC4pLCBsb25naXR1ZGUgPSBjKC05MC4sIC00Ny4pLCB0aW1lID0gYygnbGFzdCcsJ2xhc3QnKSwgZmllbGRzID0gJ2NobG9yX2EnKQoKIyBnZXQgbGF0ZXN0IDgtZGF5IGNobCAoTU9ESVMpCmNobGFJbmZvXzhkIDwtIGluZm8oJ2VyZE1IMWNobGE4ZGF5JykKTU9ESVNfQ0hMQV84ZCA8LSBncmlkZGFwKGNobGFJbmZvXzhkLCBsYXRpdHVkZSA9IGMoLTIwLiwgLTYwLiksIGxvbmdpdHVkZSA9IGMoLTkwLiwgLTQ3LiksIHRpbWUgPSBjKCdsYXN0JywnbGFzdCcpLCBmaWVsZHMgPSAnY2hsb3JvcGh5bGwnKQoKIyBNYXAgbW9udGhseSBjaGwgKFZJSVJTKQpteWNvbG9yIDwtIGNvbG9ycyRjaGxvcm9waHlsbAp3IDwtIG1hcF9kYXRhKCJ3b3JsZEhpcmVzIiwgeWxpbSA9IGMoLTYwLiwgLTIwLiksIHhsaW0gPSBjKC05MC4sIC00Ny4pKQpnZ3Bsb3QoZGF0YSA9IHZpaXJzQ0hMQSRkYXRhLCBhZXMoeCA9IGxvbiwgeSA9IGxhdCwgZmlsbCA9IGxvZyhjaGxvcl9hKSkpICsgCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSB3LCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCBmaWxsID0gImdyZXk4MCIpICsKICBnZW9tX3Jhc3RlcihpbnRlcnBvbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IG15Y29sb3IsIG5hLnZhbHVlID0gTkEpICsKICB0aGVtZV9idygpICsgeWxhYigibGF0aXR1ZGUiKSArIHhsYWIoImxvbmdpdHVkZSIpICsKICBjb29yZF9maXhlZCgxLjMsIHhsaW0gPSBjKC05MC4sIC00Ny4pLCAgeWxpbSA9IGMoLTYwLiwgLTIwLikpICsgZ2d0aXRsZSgiTGF0ZXN0IFZJSVJTIE1vbnRobHkgQ2hsYSIpCgojIE1hcCA4LWRheSBjaGwgKE1PRElTKQpteWNvbG9yIDwtIGNvbG9ycyRjaGxvcm9waHlsbAp3IDwtIG1hcF9kYXRhKCJ3b3JsZEhpcmVzIiwgeWxpbSA9IGMoLTYwLiwgLTIwLiksIHhsaW0gPSBjKC05MC4sIC00Ny4pKQpnZ3Bsb3QoZGF0YSA9IE1PRElTX0NITEFfOGQkZGF0YSwgYWVzKHggPSBsb24sIHkgPSBsYXQsIGZpbGwgPSBsb2coY2hsb3JvcGh5bGwpKSkgKyAKICBnZW9tX3BvbHlnb24oZGF0YSA9IHcsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGZpbGwgPSAiZ3JleTgwIikgKwogIGdlb21fcmFzdGVyKGludGVycG9sYXRlID0gRkFMU0UpICsKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gbXljb2xvciwgbmEudmFsdWUgPSBOQSkgKwogIHRoZW1lX2J3KCkgKyB5bGFiKCJsYXRpdHVkZSIpICsgeGxhYigibG9uZ2l0dWRlIikgKwogIGNvb3JkX2ZpeGVkKDEuMywgeGxpbSA9IGMoLTkwLiwgLTQ3LiksICB5bGltID0gYygtNjAuLCAtMjAuKSkgKyBnZ3RpdGxlKCJMYXRlc3QgTU9ESVMgOC1kYXkgQ2hsYSIpCmBgYAoK