This notebook demonstrates how easy it is to retrieve data relevant to analysing environmental drivers of small pelagics using ERDDAP servers and the R package rerddap. In the examples, the code needed to actually get the data, which is short and simple, are separated from code to map or graph the data once in the R workspace, so that the more complicated plotting code doesn’t obscure how simple it is to extract the desired data.

ERDDAP servers provide access to literally petabytes of data, including satellite data, fisheries survey data, glider data, animal tracking data and more.

require("akima")
Loading required package: akima
require("dplyr")
Loading required package: dplyr

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
require("ggplot2")
Loading required package: ggplot2
require("mapdata")
Loading required package: mapdata
Loading required package: maps
require("maps")
require("plot3D")
Loading required package: plot3D
require("rerddap")
Loading required package: rerddap

MUR SST

MUR (Multi-scale Ultra-high Resolution) is an analyzed SST product at 0.01-degree resolution going back to 2002, providing one of the longest satellite based time series at such high resolution (see https://podaac.jpl.nasa.gov/dataset/MUR-JPL-L4-GLOB-v4.1). We extract the latest data available for a region off the west coast.

require("rerddap")
sstInfo <- info('jplMURSST41')
# get latest daily sst
murSST <- griddap(sstInfo, latitude = c(22., 51.), longitude = c(-140., -105), time = c('last','last'), fields = 'analysed_sst')

and plot the results:

require("ggplot2")
require("mapdata")
mycolor <- colors$temperature
w <- map_data("worldHires", ylim = c(22., 51.), xlim = c(-140, -105))
ggplot(data = murSST$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(-140, -105),  ylim = c(22., 51.)) + ggtitle("Latest MUR SST")

VIIRS SST and Chlorophyll

VIIRS (Visible Infrared Imaging Radiometer Suite) is a scanning radiometer, that collects visible and infrared imagery and radiometric measurements of the land, atmosphere, cryosphere, and oceans. VIIRS data is used to measure cloud and aerosol properties, ocean color, sea and land surface temperature, ice motion and temperature, fires, and Earth’s albedo. Both NASA and NOAA provide VIIRS-based high resolution SST and chlorophyll products.

We look at the latest 3-day composite SST product at 750 meter resolution developed by ERD from a real-time NOAA product (see http://coastwatch.noaa.gov/cwn/cw_products_sst.html):

require("rerddap")
sstInfo <- info('erdVHsstaWS3day')
# get latest 3-day composite sst
viirsSST <- griddap(sstInfo, latitude = c(41., 31.), longitude = c(-128., -115), time = c('last','last'), fields = 'sst')

and plot the results. Note that R sees the latitude-longitude grid as slightly uneven (even though it is in fact even), and that produces artificial lines in ggplot2::geom_raster(). In order to remove those lines, the latitude-longitude grid is remapped to an evenly-space grid.

require("ggplot2")
require("mapdata")
# remap latitiudes and longitudes to even grid
myLats <- unique(viirsSST$data$lat)
myLons <- unique(viirsSST$data$lon)
myLats <- seq(range(myLats)[1], range(myLats)[2], length.out = length(myLats))
myLons <- seq(range(myLons)[1], range(myLons)[2], length.out = length(myLons))
# melt these out to full grid
mapFrame <- expand.grid(x = myLons, y = myLats)
mapFrame$y <- rev(mapFrame$y)
# form a frame with the new values and the data
tempFrame <- data.frame(sst = viirsSST$data$sst, lat = mapFrame$y, lon = mapFrame$x)
mycolor <- colors$temperature
w <- map_data("worldHires", ylim = c(30., 42.), xlim = c(-128, -114))
ggplot(data = tempFrame, aes(x = lon, y = lat, fill = 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(-128, -114),  ylim = c(30., 42.)) + ggtitle("Latest VIIRS 3-day SST")

We can obtain a time series at a location, here (36., -126.):

require("rerddap")
viirsSST1 <- griddap(sstInfo, latitude = c(36., 36.), longitude = c(-126., -126.), time = c('2015-01-01','2015-12-31'), fields = 'sst')
tempTime <- as.Date(viirsSST1$data$time, origin = '1970-01-01', tz = "GMT")
tempFrame <- data.frame(time = tempTime, sst = viirsSST1$data$sst)

and plot the time series:

require("ggplot2")
ggplot(tempFrame, aes(time, sst)) + geom_line() + theme_bw() + ylab("sst") + ggtitle("VIIRS SST at (36N, 126W)")

We look at a similar 3-day composite for chloropyll for the same region from a scientific quality product developed by NOAA (see http://coastwatch.noaa.gov/cwn/cw_products_sst.html):

require("rerddap")
chlaInfo <- info('erdVHNchla3day')
viirsCHLA <- griddap(chlaInfo, latitude = c(41., 31.), longitude = c(-128., -115), time = c('last','last'), fields = 'chla')

and plot the result:

require("ggplot2")
require("mapdata")
mycolor <- colors$chlorophyll
w <- map_data("worldHires", ylim = c(30., 42.), xlim = c(-128, -114))
ggplot(data = viirsCHLA$data, aes(x = lon, y = lat, fill = log(chla))) + 
    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(-128, -114),  ylim = c(30., 42.)) + ggtitle("Latest VIIRS 3-day Chla")

Temperature at 70m in the north Pacific from the SODA model output

This is an example of an extract from a 4-D dataset (results from the “Simple Ocean Data Assimilation (SODA)” model - - see http://www.atmos.umd.edu/~ocean/), and illustrate the case where the z-coordinate does not have the default name “altitude”. Water temperature at 70m depth is extracted for the North Pacific Ocean:

require("rerddap")
dataInfo <- rerddap::info('hawaii_d90f_20ee_c4cb')
xpos <- c(135.25, 240.25)
ypos <- c(20.25, 60.25)
zpos <- c(70.02, 70.02)
tpos <- c('2010-12-15', '2010-12-15')
soda70 <- griddap(dataInfo,  longitude = xpos, latitude = ypos, time = tpos, depth = zpos, fields = 'temp' )
str(soda70$data)
'data.frame':   17091 obs. of  4 variables:
 $ time: chr  "2010-12-15T00:00:00Z" "2010-12-15T00:00:00Z" "2010-12-15T00:00:00Z" "2010-12-15T00:00:00Z" ...
 $ lat : num  20.2 20.2 20.2 20.2 20.2 ...
 $ lon : num  135 136 136 137 137 ...
 $ temp: num  27.1 27.1 27.5 27.8 28.2 ...

Since the data cross the dateline, it is necessary to use the new “world2Hires” continental outlines in the package mapdata which is Pacific Ocean centered. Unfortunatley there is a small problem where the outlines from certain countries wrap and mistakenly appear in plots, and those countries must be removed, see code below.

require("ggplot2")
require("mapdata")
require("maps")
xlim <- c(135, 240)
ylim <- c(20, 60)
my.col <- colors$temperature
## Must do a kludge to remove countries that wrap and mess up the plot
w1 <- map("world2Hires", xlim = c(135, 240), ylim = c(20, 60), fill = TRUE, plot = FALSE)
remove <- c("UK:Great Britain", "France", "Spain", "Algeria", "Mali", "Burkina Faso", "Ghana", "Togo")
w <- map_data("world2Hires", regions = w1$names[!(w1$names %in% remove)], ylim = ylim, xlim = xlim)
myplot <- ggplot() + 
    geom_raster(data = soda70$data, aes(x = lon, y = lat, fill = temp), interpolate = FALSE) + 
    geom_polygon(data = w, aes(x = long, y = lat, group = group), fill = "grey80") +
    theme_bw() + scale_fill_gradientn(colours = my.col, na.value = NA, limits = c(-3,30), name = "temperature") +
    ylab("latitude") + xlab("longitude") +
    coord_fixed(1.3, xlim = xlim, ylim = ylim) + 
    ggtitle(paste("temperature at 70 meters depth from SODA for", soda70$time[1]))
myplot

IFREMER

The French agency IFREMER also has an ERDDAP server. We obtain salinity data at 75 meters from “Global Ocean, Coriolis Observation Re-Analysis CORA4.1” model off the west coast of the United States.

require("rerddap")
urlBase <- "http://www.ifremer.fr/erddap/"
parameter <- "PSAL"
ifrTimes <- c("2013-05-15", "2013-05-15")
ifrLats <- c(30., 50.)
ifrLons <- c(-140., -110.)
ifrDepth <- c(75., 75.)
dataInfo <- rerddap::info("ifremer_tds0_6080_109e_ed80", url = urlBase)
ifrPSAL <- griddap(dataInfo, longitude = ifrLons, latitude = ifrLats, time = ifrTimes, depth = ifrDepth,  fields = parameter, url = urlBase)
str(ifrPSAL$data)
'data.frame':   3294 obs. of  4 variables:
 $ time: chr  "2013-05-15T00:00:00Z" "2013-05-15T00:00:00Z" "2013-05-15T00:00:00Z" "2013-05-15T00:00:00Z" ...
 $ lat : num  30 30 30 30 30 ...
 $ lon : num  -140 -140 -139 -138 -138 ...
 $ PSAL: num  34.9 34.9 34.9 34.9 34.9 ...

The ggplot2 function geom_raster() is not designed for unevenly spaced coordinates, as are the latitudes from this model. The function interp() from the package akima is used to interpolate the data which are then plotted.

## ggplot2 has trouble with unequal y's
 require("akima")
 require("dplyr")
 require("ggplot2")
 require("mapdata")
  xlim <- c(-140, -110)
  ylim <- c(30, 51)
## ggplot2 has trouble with unequal y's
  my.col <- colors$salinity
  tempData1 <- ifrPSAL$data$PSAL
  tempData <- array(tempData1 , 61 * 54)
  tempFrame <- data.frame(x = ifrPSAL$data$lon, y = ifrPSAL$data$lat)
  tempFrame$temp <- tempData
  tempFrame1 <- dplyr::filter(tempFrame, !is.nan(temp))
  myinterp <- akima::interp(tempFrame1$x, tempFrame1$y, tempFrame1$temp, xo = seq(min(tempFrame1$x), max(tempFrame1$x), length = 61), yo = seq(min(tempFrame1$y), max(tempFrame1$y), length = 54))
  myinterp1 <- expand.grid(x = myinterp$x, y = myinterp$y)
  myinterp1$temp <- array(myinterp$z, 61 * 54)
  w <- map_data("worldHires", ylim = ylim, xlim = xlim)
 myplot <- ggplot() +
    geom_raster(data = myinterp1, aes(x = x, y = y, fill = temp), interpolate = FALSE) +
    geom_polygon(data = w, aes(x = long, y = lat, group = group), fill = "grey80") +
    theme_bw() + scale_fill_gradientn(colours = my.col, na.value = NA, limits = c(32, 35), name = "salinity") +
    ylab("latitude") + xlab("longitude") +
    coord_fixed(1.3, xlim = xlim, ylim = ylim) + ggtitle(paste("salinity at 75 meters",ifrPSAL$data$time[1] ))
 myplot

CalCOFI data

CalCOFI (California Cooperative Oceanic Fisheries Investigations - http://www.calcofi.org) is a multi-agency partnership formed in 1949 to investigate the collapse of the sardine population off California. The organization’s members are from NOAA Fisheries Service, Scripps Institution of Oceanography, and California Department of Fish and Wildlife. The scope of this research has evolved into the study of marine ecosystems off California and the management of its fisheries resources. The nearly complete CalCOFI data, both physical and biological, are available through ERDDAP.

The following example is a modification of a script developed by Dr. Andrew Leising of the Southwest Fisheries Science Center. The original script has been used to automate the generation of several yearly reports about the California Current Ecosystem. The script gets chlorophyll and pp data from the hydrocasts, and then calculates a seasoanlly adjusted chlorophyll anomaly as well as a seasonally adjusted pp. The first step is to get the information about the particular dataset (see http://coastwatch.pfeg.noaa.gov/erddap/tabledap/siocalcofiHydroCasts.html)

require("rerddap")
hydroInfo <- info('siocalcofiHydroCasts')

And then get the desired data form 1984 through 2014:

require("rerddap")
calcofi.df <- tabledap(hydroInfo, fields = c('cst_cnt',  'date', 'year', 'month', 'julian_date', 'julian_day', 'rpt_line', 'rpt_sta', 'cruz_num', 'intchl', 'intc14', 'time'), 'time>=1984-01-01T00:00:00Z', 'time<=2014-04-17T05:35:00Z')
str(calcofi.df)
Classes ‘tabledap’ and 'data.frame':    11072 obs. of  12 variables:
 $ cst_cnt    : int  22522 22523 22524 22525 22526 22527 22528 22529 22530 22531 ...
 $ date       : chr  "01/04/1984" "01/05/1984" "01/05/1984" "01/05/1984" ...
 $ year       : int  1984 1984 1984 1984 1984 1984 1984 1984 1984 1984 ...
 $ month      : int  1 1 1 1 1 1 1 1 1 1 ...
 $ julian_date: int  30685 30686 30686 30686 30686 30686 30687 30687 30687 30687 ...
 $ julian_day : int  4 5 5 5 5 5 6 6 6 6 ...
 $ rpt_line   : num  93.3 90 90 90 90 90 90 90 90 90 ...
 $ rpt_sta    : num  29 35 30 28 32 37 53 53 53 53 ...
 $ cruz_num   : chr  "8401" "8401" "8401" "8401" ...
 $ intchl     : num  NaN 30.3 27.9 29.9 39.3 36.7 30.3 30.6 21.3 31.1 ...
 $ intc14     : chr  "NaN" "NaN" "NaN" "NaN" ...
 $ time       : chr  "1984-01-04T22:08:00Z" "1984-01-05T03:20:00Z" "1984-01-05T09:03:00Z" "1984-01-05T12:26:00Z" ...
 - attr(*, "datasetid")= chr "siocalcofiHydroCasts"
 - attr(*, "path")= chr "~/.rerddap/f2de809f52cd97bd2d8b8caf2e7cd06d.csv"
 - attr(*, "url")= chr "http://upwell.pfeg.noaa.gov/erddap/tabledap/siocalcofiHydroCasts.csv?cst_cnt,date,year,month,julian_date,julian_day,rpt_line,rp"| __truncated__

Both “intchl” and “intC14” are characters, and they are easier to work with as numbers:

calcofi.df$cruz_num <- as.numeric(calcofi.df$cruz_num)
NAs introduced by coercion
calcofi.df$intc14 <- as.numeric(calcofi.df$intc14)
calcofi.df$time <- as.Date(calcofi.df$time, origin = '1970-01-01', tz = "GMT")

At this point the requested data are in the R workspace - the rest of the code are calculations get the seasonally adjusted values and plot them.

require("dplyr")
# calculate cruise means
by_cruznum <- group_by(calcofi.df, cruz_num)
tempData <- select(by_cruznum, year, month, cruz_num, intchl, intc14)
CruiseMeans <- summarize(by_cruznum, cruisechl = mean(intchl, na.rm = TRUE), cruisepp = mean(intc14, na.rm = TRUE), year = median(year, na.rm = TRUE), month = median(month, na.rm = TRUE))
tempTimes <- paste0(CruiseMeans$year,'-',CruiseMeans$month,'-1')
cruisetimes <- as.Date(tempTimes, origin = '1970-01-01', tz = "GMT")
CruiseMeans$cruisetimes <- cruisetimes
# calculate monthly "climatologies"
byMonth <- group_by(CruiseMeans, month)
climate <- summarize(byMonth, ppClimate = mean(cruisepp, na.rm = TRUE), chlaClimate = mean(cruisechl, na.rm = TRUE))
# calculate anomalies
CruiseMeans$chlanom <- CruiseMeans$cruisechl - climate$chlaClimate[CruiseMeans$month]
CruiseMeans$ppanom <- CruiseMeans$cruisepp - climate$ppClimate[CruiseMeans$month]
# calculate mean yearly anomaly
byYear <- select(CruiseMeans, year)
tempData <- select(CruiseMeans, year, chlanom, ppanom )
byYear <- group_by(tempData, year)
yearlyAnom <- summarize(byYear, ppYrAnom = mean(ppanom, na.rm = TRUE), chlYrAnom = mean(chlanom, na.rm = TRUE))
yearlyAnom$year <- ISOdate(yearlyAnom$year, 01, 01, hour = 0)
ggplot(yearlyAnom, aes(year, chlYrAnom)) + geom_line() + 
  theme_bw() + ggtitle('yearly chla anom')

ggplot(yearlyAnom, aes(year, ppYrAnom)) + geom_line() + 
  theme_bw() + ggtitle('yearly pp anom')

CPS Trawl Surveys

The CPS (Coastal Pelagic Species) Trawl Life History Length Frequency Data contains the length distribution of a subset of individuals from a species (mainly non-target) caught during SWFSC-FRD fishery independent trawl surveys of coastal pelagic species. Measured lengths for indicated length type (fork, standard, total, or mantle) were grouped in 10 mm bins (identified by the midpoint of the length class) and counts are recorded by sex.

We will look at the number and location of sardines (Sardinops sagax) in the tows in March 2010 and 2011, and compare with monthly SST from satellites. First we query the ERDDAP server to see if CPS Trawl data are available through the ERDDAP server, and if so, get the datasetID for the data we want.

require("rerddap")
(CPSinfo <- info('FRDCPSTrawlLHHaulCatch'))
<ERDDAP info> FRDCPSTrawlLHHaulCatch 
 Variables:  
     collection: 
         Range: 2003, 3623 
     cruise: 
         Range: 200307, 201604 
     haul: 
         Range: 1, 160 
     haulback_time: 
         Range: -6.5759508E8, 1.3977129E9 
         Units: seconds since 1970-01-01T00:00:00Z 
     itis_tsn: 
     latitude: 
         Range: 30.7001, 54.3997 
         Units: degrees_north 
     longitude: 
         Range: -134.0793, -117.3796 
         Units: degrees_east 
     remaining_weight: 
         Units: kg 
     scientific_name: 
     ship: 
     ship_spd_through_water: 
         Units: knot 
     stop_latitude: 
         Range: 30.6663, 51.0923 
     stop_longitude: 
         Range: -132.184, -117.4116 
     subsample_count: 
     subsample_weight: 
         Units: kg 
     surface_temp: 
         Units: degree C 
     surface_temp_method: 
     time: 
         Range: 1.05771978E9, 1.4613093E9 
         Units: seconds since 1970-01-01T00:00:00Z 
require("dplyr")
require("rerddap")
sardines <- tabledap(CPSinfo, fields = c('latitude',  'longitude', 'time', 'scientific_name', 'subsample_count'), 'time>=2010-01-01', 'time<=2012-01-01', 'scientific_name="Sardinops sagax"' )
sardines$time <- as.Date(sardines$time, origin = '1970-01-01', tz = "GMT")
sardines$latitude <- as.numeric(sardines$latitude)
sardines$longitude <- as.numeric(sardines$longitude)
sardine2010 <- filter(sardines, time < as.Date('2010-12-01'))

then we get monthly MODIS SST for those time periods:

require("rerddap")
# get the dataset info
sstInfo <- info('erdMWsstdmday')
# get 201004 monthly sst
sst201004 <- griddap('erdMWsstdmday', latitude = c(22., 51.), longitude = c(220., 255), time = c('2010-04-16','2010-04-16'), fields = 'sst')
# get 201104 monthly sst
sst201104 <- griddap('erdMWsstdmday', latitude = c(22., 51.), longitude = c(220., 255), time = c('2011-04-16','2011-04-16'), fields = 'sst')

and plot the sardine counts on the monthly SST:

require("dplyr")
require("ggplot2")
require("mapdata")
# get polygons of coast for this area
w <- map_data("worldHires", ylim = c(22., 51.), xlim = c(220 - 360, 250 - 360))
# plot 201004 sst on the map
sardine2010 <- filter(sardines, time < as.Date('2010-12-01', origin = '1970-01-01', tz = "GMT"))
sardine2011 <- filter(sardines, time > as.Date('2010-12-01', origin = '1970-01-01', tz = "GMT"))
mycolor <- colors$temperature
p1 <- ggplot() + 
  geom_polygon(data = w, aes(x = long, y = lat, group = group), fill = "grey80") +
  geom_raster(data = sst201004$data, aes(x = lon - 360, y = lat, fill = sst), interpolate = FALSE) +
  scale_fill_gradientn(colours = mycolor, na.value = NA, limits = c(5,30)) +
  theme_bw() + ylab("latitude") + xlab("longitude") +
  coord_fixed(1.3, xlim = c(220 - 360, 250 - 360),  ylim = c(22., 51.))
# plot 201104 sst on the map
p2 <- ggplot() + 
  geom_polygon(data = w, aes(x = long, y = lat, group = group), fill = "grey80") +
  geom_raster(data = sst201104$data, aes(x = lon - 360, y = lat, fill = sst), interpolate = FALSE) +
  geom_point(data = sardine2011, aes(x = longitude, y = latitude, colour = subsample_count)) +
  scale_fill_gradientn(colours = mycolor, na.value = NA, limits = c(5,30)) +
  theme_bw() + ylab("latitude") + xlab("longitude") +
  coord_fixed(1.3, xlim = c(220 - 360, 250 - 360),  ylim = c(22., 51.))
p1 + geom_point(data = sardine2010, aes(x = longitude, y = latitude, colour = subsample_count)) + scale_colour_gradient(space = "Lab", na.value = NA, limits = c(0,80))

p2 +   geom_point(data = sardine2011, aes(x = longitude, y = latitude, colour = subsample_count)) + scale_colour_gradient(space = "Lab", na.value = NA, limits = c(0,80))

We can also look at the distribution of sardines through the years:

require("rerddap")
sardinops <- tabledap(CPSinfo, fields = c('longitude', 'latitude', 'time'),  'scientific_name="Sardinops sagax"')
sardinops$time <- as.Date(sardinops$time, origin = '1970-01-01', tz = "GMT")
sardinops$year <- as.factor(format(sardinops$time, '%Y'))
sardinops$latitude <- as.numeric(sardinops$latitude)
sardinops$longitude <- as.numeric(sardinops$longitude)

and plot the results, with a different color for each year:

require("ggplot2")
require("mapdata")
xlim <- c(-135, -110)
ylim <- c(30, 51)
coast <- map_data("worldHires", ylim = ylim, xlim = xlim)
ggplot() + 
    geom_point(data = sardinops, aes(x = longitude, y = latitude, colour = year)) +
    geom_polygon(data = coast, aes(x = long, y = lat, group = group), fill = "grey80") +
    theme_bw() + ylab("latitude") + xlab("longitude") +
    coord_fixed(1.3, xlim = xlim, ylim = ylim) +
    ggtitle("Location of sardines by year in EPM Trawls")

NDBC Buoys

NOAA’s National Data Buoy Center (NDBC) collects world-wide data from buoys in the ocean. ERDDAP can be searched for the location of all buoys in a bounding box with latitudes(37N, 47N) and longitudes (124W, 121W):

# get ocation and station ID of NDBC buoys in same region
require("ggplot2")
require("mapdata")
BuoysInfo <- info('cwwcNDBCMet')
locationBuoys <- tabledap(BuoysInfo, distinct = TRUE, fields = c("station", "longitude", "latitude"), "longitude>=-124", "longitude<=-121", "latitude>=37", "latitude<=47")
locationBuoys$latitude <- as.numeric(locationBuoys$latitude)
locationBuoys$longitude <- as.numeric(locationBuoys$longitude)

and the results plotted:

require("ggplot2")
require("mapdata")
xlim <- c(-130, -110)
ylim <- c(35, 50)
coast <- map_data("worldHires", ylim = ylim, xlim = xlim)
ggplot() + 
   geom_point(data = locationBuoys, aes(x = longitude , y = latitude, colour = factor(station) )) + 
   geom_polygon(data = coast, aes(x = long, y = lat, group = group), fill = "grey80") +
   theme_bw() + ylab("latitude") + xlab("longitude") +
   coord_fixed(1.3, xlim = xlim, ylim = ylim) +
   ggtitle("Location of buoys in given region")

Looking at wind speed for 2012 for station “46012”

require("rerddap")
buoyData <- tabledap(BuoysInfo, fields = c("time", "wspd"), 'station="46012"', 'time>=2012-01-01', 'time<=2013-01-01')
buoyData$wspd <- as.numeric(buoyData$wspd)
buoyData$time <- as.Date(buoyData$time, origin = '1970-01-01', tz = "GMT")
ggplot(buoyData, aes(time, wspd)) + geom_line() + theme_bw() + ylab("wind speed") +
      ggtitle("Wind Speed in 2012 from buoy 46012 ")

IOOS Glider Data

The mission of the IOOS Glider DAC is to provide glider operators with a simple process for submitting glider data sets to a centralized location, enabling the data to be visualized, analyzed, widely distributed via existing web services and the Global Telecommunications System (GTS) and archived at the National Centers for Environmental Information (NCEI). The IOOS Glider Dac is accessible through rerddap (http://data.ioos.us/gliders/erddap/). Extracting and plotting salinity from part of the path of one glider deployed by the Scripps Institution of Oceanography:

require("rerddap")
urlBase <- "https://data.ioos.us/gliders/erddap/"
gliderInfo <- info("sp064-20161214T1913",  url = urlBase)
glider <- tabledap(gliderInfo, fields = c("longitude", "latitude", "depth", "salinity"), 'time>=2016-12-14', 'time<=2016-12-23', url = urlBase)
glider$longitude <- as.numeric(glider$longitude)
glider$latitude <- as.numeric(glider$latitude)
glider$depth <- as.numeric(glider$depth)

and draw a 3-D plot of the track:

require("plot3D")
scatter3D(x = glider$longitude , y = glider$latitude , z = -glider$depth, colvar = glider$salinity,              col = colors$salinity, phi = 40, theta = 25, bty = "g", type = "p", 
           ticktype = "detailed", pch = 10, clim = c(33.2,34.31), clab = 'Salinity', 
           xlab = "longitude", ylab = "latitude", zlab = "depth",
           cex = c(0.5, 1, 1.5))

LS0tCnRpdGxlOiAiRWFzeSBhY2Nlc3MgdG8gZW52aXJvbm1lbnRhbCBkYXRhIGZvciBhbmFseXppbmcgZW52aXJvbm1lbnRhbCBkcml2ZXJzIG9mIHRoZSBkeW5hbWljcyBvZiBzbWFsbCBwZWxhZ2ljIGZpc2giCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KClRoaXMgbm90ZWJvb2sgZGVtb25zdHJhdGVzIGhvdyBlYXN5IGl0IGlzIHRvIHJldHJpZXZlIGRhdGEgcmVsZXZhbnQgdG8gYW5hbHlzaW5nIGVudmlyb25tZW50YWwgZHJpdmVycyBvZiBzbWFsbCBwZWxhZ2ljcyB1c2luZyBFUkREQVAgc2VydmVycyBhbmQgdGhlIFIgcGFja2FnZSBgcmVyZGRhcGAuICBJbiB0aGUgZXhhbXBsZXMsICB0aGUgY29kZSBuZWVkZWQgdG8gYWN0dWFsbHkgZ2V0IHRoZSBkYXRhLCB3aGljaCBpcyBzaG9ydCBhbmQgc2ltcGxlLCAgYXJlIHNlcGFyYXRlZCBmcm9tIGNvZGUgdG8gbWFwIG9yIGdyYXBoIHRoZSBkYXRhIG9uY2UgaW4gdGhlIFIgd29ya3NwYWNlLCBzbyB0aGF0IHRoZSBtb3JlIGNvbXBsaWNhdGVkIHBsb3R0aW5nIGNvZGUgZG9lc24ndCBvYnNjdXJlIGhvdyBzaW1wbGUgaXQgaXMgdG8gZXh0cmFjdCB0aGUgZGVzaXJlZCBkYXRhLgoKRVJEREFQIHNlcnZlcnMgcHJvdmlkZSBhY2Nlc3MgdG8gbGl0ZXJhbGx5IHBldGFieXRlcyBvZiBkYXRhLCBpbmNsdWRpbmcgc2F0ZWxsaXRlIGRhdGEsIGZpc2hlcmllcyBzdXJ2ZXkgZGF0YSwgZ2xpZGVyIGRhdGEsIGFuaW1hbCB0cmFja2luZyBkYXRhIGFuZCBtb3JlLgoKYGBge3J9CnJlcXVpcmUoImFraW1hIikKcmVxdWlyZSgiZHBseXIiKQpyZXF1aXJlKCJnZ3Bsb3QyIikKcmVxdWlyZSgibWFwZGF0YSIpCnJlcXVpcmUoIm1hcHMiKQpyZXF1aXJlKCJwbG90M0QiKQpyZXF1aXJlKCJyZXJkZGFwIikKCmBgYAoKCiMjIyBNVVIgU1NUCgpNVVIgKE11bHRpLXNjYWxlIFVsdHJhLWhpZ2ggUmVzb2x1dGlvbikgaXMgYW4gYW5hbHl6ZWQgU1NUIHByb2R1Y3QgYXQgMC4wMS1kZWdyZWUgcmVzb2x1dGlvbiBnb2luZyBiYWNrIHRvIDIwMDIsIHByb3ZpZGluZyBvbmUgb2YgdGhlIGxvbmdlc3Qgc2F0ZWxsaXRlIGJhc2VkIHRpbWUgc2VyaWVzIGF0IHN1Y2ggaGlnaCByZXNvbHV0aW9uIChzZWUgaHR0cHM6Ly9wb2RhYWMuanBsLm5hc2EuZ292L2RhdGFzZXQvTVVSLUpQTC1MNC1HTE9CLXY0LjEpLiBXZSBleHRyYWN0IHRoZSBsYXRlc3QgZGF0YSBhdmFpbGFibGUgZm9yIGEgcmVnaW9uIG9mZiB0aGUgd2VzdCBjb2FzdC4KCmBgYHtyIE1VUmdldH0KcmVxdWlyZSgicmVyZGRhcCIpCnNzdEluZm8gPC0gaW5mbygnanBsTVVSU1NUNDEnKQojIGdldCBsYXRlc3QgZGFpbHkgc3N0Cm11clNTVCA8LSBncmlkZGFwKHNzdEluZm8sIGxhdGl0dWRlID0gYygyMi4sIDUxLiksIGxvbmdpdHVkZSA9IGMoLTE0MC4sIC0xMDUpLCB0aW1lID0gYygnbGFzdCcsJ2xhc3QnKSwgZmllbGRzID0gJ2FuYWx5c2VkX3NzdCcpCgpgYGAKCmFuZCBwbG90IHRoZSByZXN1bHRzOgoKYGBge3IgTVVScGxvdCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDMsIGZpZy5hbGlnbiA9ICdjZW50ZXInLCB3YXJuaW5nID0gRkFMU0V9CnJlcXVpcmUoImdncGxvdDIiKQpyZXF1aXJlKCJtYXBkYXRhIikKbXljb2xvciA8LSBjb2xvcnMkdGVtcGVyYXR1cmUKdyA8LSBtYXBfZGF0YSgid29ybGRIaXJlcyIsIHlsaW0gPSBjKDIyLiwgNTEuKSwgeGxpbSA9IGMoLTE0MCwgLTEwNSkpCmdncGxvdChkYXRhID0gbXVyU1NUJGRhdGEsIGFlcyh4ID0gbG9uLCB5ID0gbGF0LCBmaWxsID0gYW5hbHlzZWRfc3N0KSkgKyAKICAgIGdlb21fcG9seWdvbihkYXRhID0gdywgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgZmlsbCA9ICJncmV5ODAiKSArCiAgICBnZW9tX3Jhc3RlcihpbnRlcnBvbGF0ZSA9IEZBTFNFKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gbXljb2xvciwgbmEudmFsdWUgPSBOQSkgKwogICAgdGhlbWVfYncoKSArIHlsYWIoImxhdGl0dWRlIikgKyB4bGFiKCJsb25naXR1ZGUiKSArCiAgICBjb29yZF9maXhlZCgxLjMsIHhsaW0gPSBjKC0xNDAsIC0xMDUpLCAgeWxpbSA9IGMoMjIuLCA1MS4pKSArIGdndGl0bGUoIkxhdGVzdCBNVVIgU1NUIikKYGBgCgojIyMgVklJUlMgU1NUIGFuZCBDaGxvcm9waHlsbAoKVklJUlMgKFZpc2libGUgSW5mcmFyZWQgSW1hZ2luZyBSYWRpb21ldGVyIFN1aXRlKSAgaXMgYSBzY2FubmluZyByYWRpb21ldGVyLCB0aGF0IGNvbGxlY3RzIHZpc2libGUgYW5kIGluZnJhcmVkIGltYWdlcnkgYW5kIHJhZGlvbWV0cmljIG1lYXN1cmVtZW50cyBvZiB0aGUgbGFuZCwgYXRtb3NwaGVyZSwgY3J5b3NwaGVyZSwgYW5kIG9jZWFucy4gVklJUlMgZGF0YSBpcyB1c2VkIHRvIG1lYXN1cmUgY2xvdWQgYW5kIGFlcm9zb2wgcHJvcGVydGllcywgb2NlYW4gY29sb3IsIHNlYSBhbmQgbGFuZCBzdXJmYWNlIHRlbXBlcmF0dXJlLCBpY2UgbW90aW9uIGFuZCB0ZW1wZXJhdHVyZSwgZmlyZXMsIGFuZCBFYXJ0aCdzIGFsYmVkby4gICBCb3RoIE5BU0EgYW5kIE5PQUEgcHJvdmlkZSBWSUlSUy1iYXNlZCBoaWdoIHJlc29sdXRpb24gU1NUIGFuZCBjaGxvcm9waHlsbCBwcm9kdWN0cy4KCldlIGxvb2sgYXQgdGhlIGxhdGVzdCAzLWRheSBjb21wb3NpdGUgU1NUIHByb2R1Y3QgYXQgNzUwIG1ldGVyIHJlc29sdXRpb24gZGV2ZWxvcGVkIGJ5IEVSRCBmcm9tIGEgcmVhbC10aW1lIE5PQUEgcHJvZHVjdCAoc2VlIGh0dHA6Ly9jb2FzdHdhdGNoLm5vYWEuZ292L2N3bi9jd19wcm9kdWN0c19zc3QuaHRtbCk6ICAKCmBgYHtyIFZJSVJTZ2V0fQpyZXF1aXJlKCJyZXJkZGFwIikKc3N0SW5mbyA8LSBpbmZvKCdlcmRWSHNzdGFXUzNkYXknKQojIGdldCBsYXRlc3QgMy1kYXkgY29tcG9zaXRlIHNzdAp2aWlyc1NTVCA8LSBncmlkZGFwKHNzdEluZm8sIGxhdGl0dWRlID0gYyg0MS4sIDMxLiksIGxvbmdpdHVkZSA9IGMoLTEyOC4sIC0xMTUpLCB0aW1lID0gYygnbGFzdCcsJ2xhc3QnKSwgZmllbGRzID0gJ3NzdCcpCgpgYGAKCmFuZCBwbG90IHRoZSByZXN1bHRzLiBOb3RlIHRoYXQgPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWUiPlI8L3NwYW4+IHNlZXMgdGhlIGxhdGl0dWRlLWxvbmdpdHVkZSBncmlkIGFzIHNsaWdodGx5IHVuZXZlbiAoZXZlbiB0aG91Z2ggaXQgaXMgaW4gZmFjdCBldmVuKSwgYW5kIHRoYXQgcHJvZHVjZXMgYXJ0aWZpY2lhbCBsaW5lcyBpbiBgZ2dwbG90Mjo6Z2VvbV9yYXN0ZXIoKWAuICBJbiBvcmRlciB0byByZW1vdmUgdGhvc2UgbGluZXMsIHRoZSBsYXRpdHVkZS1sb25naXR1ZGUgZ3JpZCBpcyByZW1hcHBlZCB0byBhbiBldmVubHktc3BhY2UgZ3JpZC4KCgpgYGB7ciBWSUlSU3Bsb3QsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSAzLCBmaWcuYWxpZ24gPSAnY2VudGVyJywgd2FybmluZyA9IEZBTFNFfQpyZXF1aXJlKCJnZ3Bsb3QyIikKcmVxdWlyZSgibWFwZGF0YSIpCiMgcmVtYXAgbGF0aXRpdWRlcyBhbmQgbG9uZ2l0dWRlcyB0byBldmVuIGdyaWQKbXlMYXRzIDwtIHVuaXF1ZSh2aWlyc1NTVCRkYXRhJGxhdCkKbXlMb25zIDwtIHVuaXF1ZSh2aWlyc1NTVCRkYXRhJGxvbikKbXlMYXRzIDwtIHNlcShyYW5nZShteUxhdHMpWzFdLCByYW5nZShteUxhdHMpWzJdLCBsZW5ndGgub3V0ID0gbGVuZ3RoKG15TGF0cykpCm15TG9ucyA8LSBzZXEocmFuZ2UobXlMb25zKVsxXSwgcmFuZ2UobXlMb25zKVsyXSwgbGVuZ3RoLm91dCA9IGxlbmd0aChteUxvbnMpKQojIG1lbHQgdGhlc2Ugb3V0IHRvIGZ1bGwgZ3JpZAptYXBGcmFtZSA8LSBleHBhbmQuZ3JpZCh4ID0gbXlMb25zLCB5ID0gbXlMYXRzKQptYXBGcmFtZSR5IDwtIHJldihtYXBGcmFtZSR5KQojIGZvcm0gYSBmcmFtZSB3aXRoIHRoZSBuZXcgdmFsdWVzIGFuZCB0aGUgZGF0YQp0ZW1wRnJhbWUgPC0gZGF0YS5mcmFtZShzc3QgPSB2aWlyc1NTVCRkYXRhJHNzdCwgbGF0ID0gbWFwRnJhbWUkeSwgbG9uID0gbWFwRnJhbWUkeCkKbXljb2xvciA8LSBjb2xvcnMkdGVtcGVyYXR1cmUKdyA8LSBtYXBfZGF0YSgid29ybGRIaXJlcyIsIHlsaW0gPSBjKDMwLiwgNDIuKSwgeGxpbSA9IGMoLTEyOCwgLTExNCkpCmdncGxvdChkYXRhID0gdGVtcEZyYW1lLCBhZXMoeCA9IGxvbiwgeSA9IGxhdCwgZmlsbCA9IHNzdCkpICsgCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHcsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGZpbGwgPSAiZ3JleTgwIikgKwogICAgZ2VvbV9yYXN0ZXIoaW50ZXJwb2xhdGUgPSBGQUxTRSkgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IG15Y29sb3IsIG5hLnZhbHVlID0gTkEpICsKICAgIHRoZW1lX2J3KCkgKyB5bGFiKCJsYXRpdHVkZSIpICsgeGxhYigibG9uZ2l0dWRlIikgKwogICAgY29vcmRfZml4ZWQoMS4zLCB4bGltID0gYygtMTI4LCAtMTE0KSwgIHlsaW0gPSBjKDMwLiwgNDIuKSkgKyBnZ3RpdGxlKCJMYXRlc3QgVklJUlMgMy1kYXkgU1NUIikKCmBgYAoKV2UgY2FuIG9idGFpbiBhIHRpbWUgc2VyaWVzIGF0IGEgbG9jYXRpb24sICBoZXJlICgzNi4sIC0xMjYuKToKCmBgYHtyIFZJSVJTVFNnZXR9CnJlcXVpcmUoInJlcmRkYXAiKQp2aWlyc1NTVDEgPC0gZ3JpZGRhcChzc3RJbmZvLCBsYXRpdHVkZSA9IGMoMzYuLCAzNi4pLCBsb25naXR1ZGUgPSBjKC0xMjYuLCAtMTI2LiksIHRpbWUgPSBjKCcyMDE1LTAxLTAxJywnMjAxNS0xMi0zMScpLCBmaWVsZHMgPSAnc3N0JykKdGVtcFRpbWUgPC0gYXMuRGF0ZSh2aWlyc1NTVDEkZGF0YSR0aW1lLCBvcmlnaW4gPSAnMTk3MC0wMS0wMScsIHR6ID0gIkdNVCIpCnRlbXBGcmFtZSA8LSBkYXRhLmZyYW1lKHRpbWUgPSB0ZW1wVGltZSwgc3N0ID0gdmlpcnNTU1QxJGRhdGEkc3N0KQoKYGBgCgphbmQgcGxvdCB0aGUgdGltZSBzZXJpZXM6CgpgYGB7ciBWSUlSU1RTcGxvdCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDMsIGZpZy5hbGlnbiA9ICdjZW50ZXInLCB3YXJuaW5nID0gRkFMU0V9CnJlcXVpcmUoImdncGxvdDIiKQpnZ3Bsb3QodGVtcEZyYW1lLCBhZXModGltZSwgc3N0KSkgKyBnZW9tX2xpbmUoKSArIHRoZW1lX2J3KCkgKyB5bGFiKCJzc3QiKSArIGdndGl0bGUoIlZJSVJTIFNTVCBhdCAoMzZOLCAxMjZXKSIpCgpgYGAKCgoKV2UgbG9vayBhdCBhIHNpbWlsYXIgMy1kYXkgY29tcG9zaXRlIGZvciBjaGxvcm9weWxsIGZvciB0aGUgc2FtZSByZWdpb24gZnJvbSBhIHNjaWVudGlmaWMgcXVhbGl0eSBwcm9kdWN0IGRldmVsb3BlZCBieSBOT0FBIChzZWUgaHR0cDovL2NvYXN0d2F0Y2gubm9hYS5nb3YvY3duL2N3X3Byb2R1Y3RzX3NzdC5odG1sKToKCmBgYHtyIFZITkNIbGFnZXR9CnJlcXVpcmUoInJlcmRkYXAiKQpjaGxhSW5mbyA8LSBpbmZvKCdlcmRWSE5jaGxhM2RheScpCnZpaXJzQ0hMQSA8LSBncmlkZGFwKGNobGFJbmZvLCBsYXRpdHVkZSA9IGMoNDEuLCAzMS4pLCBsb25naXR1ZGUgPSBjKC0xMjguLCAtMTE1KSwgdGltZSA9IGMoJ2xhc3QnLCdsYXN0JyksIGZpZWxkcyA9ICdjaGxhJykKCmBgYAoKYW5kIHBsb3QgdGhlIHJlc3VsdDoKCmBgYHtyIFZITkNIbGFwbG90LCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gMywgZmlnLmFsaWduID0gJ2NlbnRlcicsIHdhcm5pbmcgPSBGQUxTRX0KcmVxdWlyZSgiZ2dwbG90MiIpCnJlcXVpcmUoIm1hcGRhdGEiKQpteWNvbG9yIDwtIGNvbG9ycyRjaGxvcm9waHlsbAp3IDwtIG1hcF9kYXRhKCJ3b3JsZEhpcmVzIiwgeWxpbSA9IGMoMzAuLCA0Mi4pLCB4bGltID0gYygtMTI4LCAtMTE0KSkKZ2dwbG90KGRhdGEgPSB2aWlyc0NITEEkZGF0YSwgYWVzKHggPSBsb24sIHkgPSBsYXQsIGZpbGwgPSBsb2coY2hsYSkpKSArIAogICAgZ2VvbV9wb2x5Z29uKGRhdGEgPSB3LCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCBmaWxsID0gImdyZXk4MCIpICsKICAgIGdlb21fcmFzdGVyKGludGVycG9sYXRlID0gRkFMU0UpICsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnMgPSBteWNvbG9yLCBuYS52YWx1ZSA9IE5BKSArCiAgICB0aGVtZV9idygpICsgeWxhYigibGF0aXR1ZGUiKSArIHhsYWIoImxvbmdpdHVkZSIpICsKICAgIGNvb3JkX2ZpeGVkKDEuMywgeGxpbSA9IGMoLTEyOCwgLTExNCksICB5bGltID0gYygzMC4sIDQyLikpICsgZ2d0aXRsZSgiTGF0ZXN0IFZJSVJTIDMtZGF5IENobGEiKQoKYGBgCgoKCgoKIyMjIFRlbXBlcmF0dXJlIGF0IDcwbSBpbiB0aGUgbm9ydGggUGFjaWZpYyBmcm9tIHRoZSBTT0RBIG1vZGVsIG91dHB1dAoKVGhpcyBpcyBhbiBleGFtcGxlIG9mIGFuIGV4dHJhY3QgZnJvbSBhIDQtRCBkYXRhc2V0IChyZXN1bHRzIGZyb20gdGhlICJTaW1wbGUgT2NlYW4gRGF0YSBBc3NpbWlsYXRpb24gKFNPREEpIiBtb2RlbCAtIC0gc2VlIGh0dHA6Ly93d3cuYXRtb3MudW1kLmVkdS9+b2NlYW4vKSwgYW5kIGlsbHVzdHJhdGUgdGhlIGNhc2Ugd2hlcmUgdGhlIHotY29vcmRpbmF0ZSBkb2VzIG5vdCBoYXZlIHRoZSBkZWZhdWx0IG5hbWUgImFsdGl0dWRlIi4gIFdhdGVyIHRlbXBlcmF0dXJlIGF0IDcwbSBkZXB0aCBpcyBleHRyYWN0ZWQgZm9yIHRoZSBOb3J0aCBQYWNpZmljIE9jZWFuOgoKCmBgYHtyIHNvZGE3MGdldH0KcmVxdWlyZSgicmVyZGRhcCIpCmRhdGFJbmZvIDwtIHJlcmRkYXA6OmluZm8oJ2hhd2FpaV9kOTBmXzIwZWVfYzRjYicpCnhwb3MgPC0gYygxMzUuMjUsIDI0MC4yNSkKeXBvcyA8LSBjKDIwLjI1LCA2MC4yNSkKenBvcyA8LSBjKDcwLjAyLCA3MC4wMikKdHBvcyA8LSBjKCcyMDEwLTEyLTE1JywgJzIwMTAtMTItMTUnKQpzb2RhNzAgPC0gZ3JpZGRhcChkYXRhSW5mbywgIGxvbmdpdHVkZSA9IHhwb3MsIGxhdGl0dWRlID0geXBvcywgdGltZSA9IHRwb3MsIGRlcHRoID0genBvcywgZmllbGRzID0gJ3RlbXAnICkKc3RyKHNvZGE3MCRkYXRhKQpgYGAKClNpbmNlIHRoZSBkYXRhIGNyb3NzIHRoZSBkYXRlbGluZSwgaXQgaXMgbmVjZXNzYXJ5IHRvIHVzZSB0aGUgbmV3ICJ3b3JsZDJIaXJlcyIgY29udGluZW50YWwgb3V0bGluZXMgaW4gdGhlIHBhY2thZ2UgYG1hcGRhdGFgIHdoaWNoIGlzIFBhY2lmaWMgT2NlYW4gY2VudGVyZWQuICBVbmZvcnR1bmF0bGV5IHRoZXJlIGlzIGEgc21hbGwgcHJvYmxlbSB3aGVyZSB0aGUgb3V0bGluZXMgZnJvbSBjZXJ0YWluIGNvdW50cmllcyB3cmFwIGFuZCBtaXN0YWtlbmx5IGFwcGVhciBpbiBwbG90cywgYW5kIHRob3NlIGNvdW50cmllcyBtdXN0IGJlIHJlbW92ZWQsICBzZWUgY29kZSBiZWxvdy4KCgpgYGB7ciBzb2RhNzBQbG90LCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gMywgZmlnLmFsaWduID0gJ2NlbnRlcicsIHdhcm5pbmcgPSBGQUxTRX0KcmVxdWlyZSgiZ2dwbG90MiIpCnJlcXVpcmUoIm1hcGRhdGEiKQpyZXF1aXJlKCJtYXBzIikKeGxpbSA8LSBjKDEzNSwgMjQwKQp5bGltIDwtIGMoMjAsIDYwKQpteS5jb2wgPC0gY29sb3JzJHRlbXBlcmF0dXJlCiMjIE11c3QgZG8gYSBrbHVkZ2UgdG8gcmVtb3ZlIGNvdW50cmllcyB0aGF0IHdyYXAgYW5kIG1lc3MgdXAgdGhlIHBsb3QKdzEgPC0gbWFwKCJ3b3JsZDJIaXJlcyIsIHhsaW0gPSBjKDEzNSwgMjQwKSwgeWxpbSA9IGMoMjAsIDYwKSwgZmlsbCA9IFRSVUUsIHBsb3QgPSBGQUxTRSkKcmVtb3ZlIDwtIGMoIlVLOkdyZWF0IEJyaXRhaW4iLCAiRnJhbmNlIiwgIlNwYWluIiwgIkFsZ2VyaWEiLCAiTWFsaSIsICJCdXJraW5hIEZhc28iLCAiR2hhbmEiLCAiVG9nbyIpCncgPC0gbWFwX2RhdGEoIndvcmxkMkhpcmVzIiwgcmVnaW9ucyA9IHcxJG5hbWVzWyEodzEkbmFtZXMgJWluJSByZW1vdmUpXSwgeWxpbSA9IHlsaW0sIHhsaW0gPSB4bGltKQpteXBsb3QgPC0gZ2dwbG90KCkgKyAKICAgIGdlb21fcmFzdGVyKGRhdGEgPSBzb2RhNzAkZGF0YSwgYWVzKHggPSBsb24sIHkgPSBsYXQsIGZpbGwgPSB0ZW1wKSwgaW50ZXJwb2xhdGUgPSBGQUxTRSkgKyAKICAgIGdlb21fcG9seWdvbihkYXRhID0gdywgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgZmlsbCA9ICJncmV5ODAiKSArCiAgICB0aGVtZV9idygpICsgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IG15LmNvbCwgbmEudmFsdWUgPSBOQSwgbGltaXRzID0gYygtMywzMCksIG5hbWUgPSAidGVtcGVyYXR1cmUiKSArCiAgICB5bGFiKCJsYXRpdHVkZSIpICsgeGxhYigibG9uZ2l0dWRlIikgKwogICAgY29vcmRfZml4ZWQoMS4zLCB4bGltID0geGxpbSwgeWxpbSA9IHlsaW0pICsgCiAgICBnZ3RpdGxlKHBhc3RlKCJ0ZW1wZXJhdHVyZSBhdCA3MCBtZXRlcnMgZGVwdGggZnJvbSBTT0RBIGZvciIsIHNvZGE3MCR0aW1lWzFdKSkKbXlwbG90CmBgYAoKIyMjIElGUkVNRVIKClRoZSBGcmVuY2ggYWdlbmN5IElGUkVNRVIgYWxzbyBoYXMgYW4gPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWUiPkVSRERBUDwvc3Bhbj4gc2VydmVyLiBXZSBvYnRhaW4gc2FsaW5pdHkgZGF0YSBhdCA3NSBtZXRlcnMgZnJvbSAiR2xvYmFsIE9jZWFuLCBDb3Jpb2xpcyBPYnNlcnZhdGlvbiBSZS1BbmFseXNpcyBDT1JBNC4xIiBtb2RlbCBvZmYgdGhlIHdlc3QgY29hc3Qgb2YgdGhlIFVuaXRlZCBTdGF0ZXMuCgpgYGB7ciBJRlJFTUVSZ2V0fQpyZXF1aXJlKCJyZXJkZGFwIikKdXJsQmFzZSA8LSAiaHR0cDovL3d3dy5pZnJlbWVyLmZyL2VyZGRhcC8iCnBhcmFtZXRlciA8LSAiUFNBTCIKaWZyVGltZXMgPC0gYygiMjAxMy0wNS0xNSIsICIyMDEzLTA1LTE1IikKaWZyTGF0cyA8LSBjKDMwLiwgNTAuKQppZnJMb25zIDwtIGMoLTE0MC4sIC0xMTAuKQppZnJEZXB0aCA8LSBjKDc1LiwgNzUuKQpkYXRhSW5mbyA8LSByZXJkZGFwOjppbmZvKCJpZnJlbWVyX3RkczBfNjA4MF8xMDllX2VkODAiLCB1cmwgPSB1cmxCYXNlKQppZnJQU0FMIDwtIGdyaWRkYXAoZGF0YUluZm8sIGxvbmdpdHVkZSA9IGlmckxvbnMsIGxhdGl0dWRlID0gaWZyTGF0cywgdGltZSA9IGlmclRpbWVzLCBkZXB0aCA9IGlmckRlcHRoLCAgZmllbGRzID0gcGFyYW1ldGVyLCB1cmwgPSB1cmxCYXNlKQpzdHIoaWZyUFNBTCRkYXRhKQoKYGBgCgpUaGUgYGdncGxvdDJgIGZ1bmN0aW9uIGBnZW9tX3Jhc3RlcigpYCBpcyBub3QgZGVzaWduZWQgZm9yIHVuZXZlbmx5IHNwYWNlZCBjb29yZGluYXRlcywgYXMgYXJlIHRoZSBsYXRpdHVkZXMgZnJvbSB0aGlzIG1vZGVsLiAgVGhlIGZ1bmN0aW9uIGBpbnRlcnAoKWAgZnJvbSB0aGUgcGFja2FnZSBgYWtpbWFgIGlzIHVzZWQgdG8gaW50ZXJwb2xhdGUgdGhlIGRhdGEgd2hpY2ggYXJlIHRoZW4gcGxvdHRlZC4KCgpgYGB7ciBpZnJQU0FMcGxvdCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDMsIGZpZy5hbGlnbj0nY2VudGVyJywgd2FybmluZyA9IEZBTFNFfQojIyBnZ3Bsb3QyIGhhcyB0cm91YmxlIHdpdGggdW5lcXVhbCB5J3MKIHJlcXVpcmUoImFraW1hIikKIHJlcXVpcmUoImRwbHlyIikKIHJlcXVpcmUoImdncGxvdDIiKQogcmVxdWlyZSgibWFwZGF0YSIpCiAgeGxpbSA8LSBjKC0xNDAsIC0xMTApCiAgeWxpbSA8LSBjKDMwLCA1MSkKIyMgZ2dwbG90MiBoYXMgdHJvdWJsZSB3aXRoIHVuZXF1YWwgeSdzCiAgbXkuY29sIDwtIGNvbG9ycyRzYWxpbml0eQogIHRlbXBEYXRhMSA8LSBpZnJQU0FMJGRhdGEkUFNBTAogIHRlbXBEYXRhIDwtIGFycmF5KHRlbXBEYXRhMSAsIDYxICogNTQpCiAgdGVtcEZyYW1lIDwtIGRhdGEuZnJhbWUoeCA9IGlmclBTQUwkZGF0YSRsb24sIHkgPSBpZnJQU0FMJGRhdGEkbGF0KQogIHRlbXBGcmFtZSR0ZW1wIDwtIHRlbXBEYXRhCiAgdGVtcEZyYW1lMSA8LSBkcGx5cjo6ZmlsdGVyKHRlbXBGcmFtZSwgIWlzLm5hbih0ZW1wKSkKICBteWludGVycCA8LSBha2ltYTo6aW50ZXJwKHRlbXBGcmFtZTEkeCwgdGVtcEZyYW1lMSR5LCB0ZW1wRnJhbWUxJHRlbXAsIHhvID0gc2VxKG1pbih0ZW1wRnJhbWUxJHgpLCBtYXgodGVtcEZyYW1lMSR4KSwgbGVuZ3RoID0gNjEpLCB5byA9IHNlcShtaW4odGVtcEZyYW1lMSR5KSwgbWF4KHRlbXBGcmFtZTEkeSksIGxlbmd0aCA9IDU0KSkKICBteWludGVycDEgPC0gZXhwYW5kLmdyaWQoeCA9IG15aW50ZXJwJHgsIHkgPSBteWludGVycCR5KQogIG15aW50ZXJwMSR0ZW1wIDwtIGFycmF5KG15aW50ZXJwJHosIDYxICogNTQpCiAgdyA8LSBtYXBfZGF0YSgid29ybGRIaXJlcyIsIHlsaW0gPSB5bGltLCB4bGltID0geGxpbSkKIG15cGxvdCA8LSBnZ3Bsb3QoKSArCiAgICBnZW9tX3Jhc3RlcihkYXRhID0gbXlpbnRlcnAxLCBhZXMoeCA9IHgsIHkgPSB5LCBmaWxsID0gdGVtcCksIGludGVycG9sYXRlID0gRkFMU0UpICsKICAgIGdlb21fcG9seWdvbihkYXRhID0gdywgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgZmlsbCA9ICJncmV5ODAiKSArCiAgICB0aGVtZV9idygpICsgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IG15LmNvbCwgbmEudmFsdWUgPSBOQSwgbGltaXRzID0gYygzMiwgMzUpLCBuYW1lID0gInNhbGluaXR5IikgKwogICAgeWxhYigibGF0aXR1ZGUiKSArIHhsYWIoImxvbmdpdHVkZSIpICsKICAgIGNvb3JkX2ZpeGVkKDEuMywgeGxpbSA9IHhsaW0sIHlsaW0gPSB5bGltKSArIGdndGl0bGUocGFzdGUoInNhbGluaXR5IGF0IDc1IG1ldGVycyIsaWZyUFNBTCRkYXRhJHRpbWVbMV0gKSkKIG15cGxvdApgYGAKCiMjIyBDYWxDT0ZJIGRhdGEKCkNhbENPRkkgKENhbGlmb3JuaWEgQ29vcGVyYXRpdmUgT2NlYW5pYyBGaXNoZXJpZXMgSW52ZXN0aWdhdGlvbnMgLSBodHRwOi8vd3d3LmNhbGNvZmkub3JnKSBpcyBhIG11bHRpLWFnZW5jeSBwYXJ0bmVyc2hpcCBmb3JtZWQgaW4gMTk0OSB0byBpbnZlc3RpZ2F0ZSB0aGUgY29sbGFwc2Ugb2YgdGhlIHNhcmRpbmUgcG9wdWxhdGlvbiBvZmYgQ2FsaWZvcm5pYS4gVGhlIG9yZ2FuaXphdGlvbidzIG1lbWJlcnMgYXJlIGZyb20gTk9BQSBGaXNoZXJpZXMgU2VydmljZSwgU2NyaXBwcyBJbnN0aXR1dGlvbiBvZiBPY2Vhbm9ncmFwaHksIGFuZCBDYWxpZm9ybmlhIERlcGFydG1lbnQgb2YgRmlzaCBhbmQgV2lsZGxpZmUuIFRoZSBzY29wZSBvZiB0aGlzIHJlc2VhcmNoIGhhcyBldm9sdmVkIGludG8gdGhlIHN0dWR5IG9mIG1hcmluZSBlY29zeXN0ZW1zIG9mZiBDYWxpZm9ybmlhIGFuZCB0aGUgbWFuYWdlbWVudCBvZiBpdHMgZmlzaGVyaWVzIHJlc291cmNlcy4gIFRoZSBuZWFybHkgY29tcGxldGUgQ2FsQ09GSSBkYXRhLCBib3RoIHBoeXNpY2FsIGFuZCBiaW9sb2dpY2FsLCBhcmUgYXZhaWxhYmxlIHRocm91Z2ggPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWUiPkVSRERBUDwvc3Bhbj4uCgpUaGUgZm9sbG93aW5nIGV4YW1wbGUgaXMgYSBtb2RpZmljYXRpb24gb2YgYSBzY3JpcHQgZGV2ZWxvcGVkIGJ5IERyLiBBbmRyZXcgTGVpc2luZyBvZiB0aGUgU291dGh3ZXN0IEZpc2hlcmllcyBTY2llbmNlIENlbnRlci4gIFRoZSBvcmlnaW5hbCBzY3JpcHQgaGFzIGJlZW4gdXNlZCB0byBhdXRvbWF0ZSB0aGUgZ2VuZXJhdGlvbiBvZiBzZXZlcmFsIHllYXJseSByZXBvcnRzIGFib3V0IHRoZSBDYWxpZm9ybmlhIEN1cnJlbnQgRWNvc3lzdGVtLiAgIFRoZSBzY3JpcHQgZ2V0cyBjaGxvcm9waHlsbCBhbmQgcHAgZGF0YSBmcm9tIHRoZSBoeWRyb2Nhc3RzLCAgYW5kIHRoZW4gY2FsY3VsYXRlcyBhIHNlYXNvYW5sbHkgYWRqdXN0ZWQgY2hsb3JvcGh5bGwgYW5vbWFseSBhcyB3ZWxsIGFzIGEgc2Vhc29uYWxseSBhZGp1c3RlZCBwcC4gIFRoZSBmaXJzdCBzdGVwIGlzIHRvIGdldCB0aGUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHBhcnRpY3VsYXIgZGF0YXNldCAoc2VlIGh0dHA6Ly9jb2FzdHdhdGNoLnBmZWcubm9hYS5nb3YvZXJkZGFwL3RhYmxlZGFwL3Npb2NhbGNvZmlIeWRyb0Nhc3RzLmh0bWwpCgpgYGB7cn0KcmVxdWlyZSgicmVyZGRhcCIpCmh5ZHJvSW5mbyA8LSBpbmZvKCdzaW9jYWxjb2ZpSHlkcm9DYXN0cycpCmBgYAoKQW5kIHRoZW4gZ2V0IHRoZSBkZXNpcmVkIGRhdGEgZm9ybSAxOTg0IHRocm91Z2ggMjAxNDoKCmBgYHtyIGNhbENPRkl9CnJlcXVpcmUoInJlcmRkYXAiKQpjYWxjb2ZpLmRmIDwtIHRhYmxlZGFwKGh5ZHJvSW5mbywgZmllbGRzID0gYygnY3N0X2NudCcsICAnZGF0ZScsICd5ZWFyJywgJ21vbnRoJywgJ2p1bGlhbl9kYXRlJywgJ2p1bGlhbl9kYXknLCAncnB0X2xpbmUnLCAncnB0X3N0YScsICdjcnV6X251bScsICdpbnRjaGwnLCAnaW50YzE0JywgJ3RpbWUnKSwgJ3RpbWU+PTE5ODQtMDEtMDFUMDA6MDA6MDBaJywgJ3RpbWU8PTIwMTQtMDQtMTdUMDU6MzU6MDBaJykKc3RyKGNhbGNvZmkuZGYpCgpgYGAKCkJvdGggImludGNobCIgYW5kICJpbnRDMTQiIGFyZSBjaGFyYWN0ZXJzLCBhbmQgdGhleSBhcmUgZWFzaWVyIHRvIHdvcmsgd2l0aCBhcyBudW1iZXJzOgoKYGBge3IgY2FsQ09GSW51bX0KY2FsY29maS5kZiRjcnV6X251bSA8LSBhcy5udW1lcmljKGNhbGNvZmkuZGYkY3J1el9udW0pCmNhbGNvZmkuZGYkaW50YzE0IDwtIGFzLm51bWVyaWMoY2FsY29maS5kZiRpbnRjMTQpCmNhbGNvZmkuZGYkdGltZSA8LSBhcy5EYXRlKGNhbGNvZmkuZGYkdGltZSwgb3JpZ2luID0gJzE5NzAtMDEtMDEnLCB0eiA9ICJHTVQiKQoKYGBgCgpBdCB0aGlzIHBvaW50IHRoZSByZXF1ZXN0ZWQgZGF0YSBhcmUgaW4gdGhlIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlIj5SPC9zcGFuPiB3b3Jrc3BhY2UgLSB0aGUgcmVzdCBvZiB0aGUgY29kZSBhcmUgY2FsY3VsYXRpb25zIGdldCB0aGUgc2Vhc29uYWxseSBhZGp1c3RlZCB2YWx1ZXMgYW5kIHBsb3QgdGhlbS4KCmBgYHtyIGNhbENPRklQbG90Y2hsYSwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDMsIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLnNob3cgPSAnaG9sZCcsIHdhcm5pbmcgPSBGQUxTRX0KcmVxdWlyZSgiZHBseXIiKQoKIyBjYWxjdWxhdGUgY3J1aXNlIG1lYW5zCmJ5X2NydXpudW0gPC0gZ3JvdXBfYnkoY2FsY29maS5kZiwgY3J1el9udW0pCnRlbXBEYXRhIDwtIHNlbGVjdChieV9jcnV6bnVtLCB5ZWFyLCBtb250aCwgY3J1el9udW0sIGludGNobCwgaW50YzE0KQpDcnVpc2VNZWFucyA8LSBzdW1tYXJpemUoYnlfY3J1em51bSwgY3J1aXNlY2hsID0gbWVhbihpbnRjaGwsIG5hLnJtID0gVFJVRSksIGNydWlzZXBwID0gbWVhbihpbnRjMTQsIG5hLnJtID0gVFJVRSksIHllYXIgPSBtZWRpYW4oeWVhciwgbmEucm0gPSBUUlVFKSwgbW9udGggPSBtZWRpYW4obW9udGgsIG5hLnJtID0gVFJVRSkpCnRlbXBUaW1lcyA8LSBwYXN0ZTAoQ3J1aXNlTWVhbnMkeWVhciwnLScsQ3J1aXNlTWVhbnMkbW9udGgsJy0xJykKY3J1aXNldGltZXMgPC0gYXMuRGF0ZSh0ZW1wVGltZXMsIG9yaWdpbiA9ICcxOTcwLTAxLTAxJywgdHogPSAiR01UIikKQ3J1aXNlTWVhbnMkY3J1aXNldGltZXMgPC0gY3J1aXNldGltZXMKIyBjYWxjdWxhdGUgbW9udGhseSAiY2xpbWF0b2xvZ2llcyIKYnlNb250aCA8LSBncm91cF9ieShDcnVpc2VNZWFucywgbW9udGgpCmNsaW1hdGUgPC0gc3VtbWFyaXplKGJ5TW9udGgsIHBwQ2xpbWF0ZSA9IG1lYW4oY3J1aXNlcHAsIG5hLnJtID0gVFJVRSksIGNobGFDbGltYXRlID0gbWVhbihjcnVpc2VjaGwsIG5hLnJtID0gVFJVRSkpCiMgY2FsY3VsYXRlIGFub21hbGllcwpDcnVpc2VNZWFucyRjaGxhbm9tIDwtIENydWlzZU1lYW5zJGNydWlzZWNobCAtIGNsaW1hdGUkY2hsYUNsaW1hdGVbQ3J1aXNlTWVhbnMkbW9udGhdCkNydWlzZU1lYW5zJHBwYW5vbSA8LSBDcnVpc2VNZWFucyRjcnVpc2VwcCAtIGNsaW1hdGUkcHBDbGltYXRlW0NydWlzZU1lYW5zJG1vbnRoXQojIGNhbGN1bGF0ZSBtZWFuIHllYXJseSBhbm9tYWx5CmJ5WWVhciA8LSBzZWxlY3QoQ3J1aXNlTWVhbnMsIHllYXIpCnRlbXBEYXRhIDwtIHNlbGVjdChDcnVpc2VNZWFucywgeWVhciwgY2hsYW5vbSwgcHBhbm9tICkKYnlZZWFyIDwtIGdyb3VwX2J5KHRlbXBEYXRhLCB5ZWFyKQp5ZWFybHlBbm9tIDwtIHN1bW1hcml6ZShieVllYXIsIHBwWXJBbm9tID0gbWVhbihwcGFub20sIG5hLnJtID0gVFJVRSksIGNobFlyQW5vbSA9IG1lYW4oY2hsYW5vbSwgbmEucm0gPSBUUlVFKSkKeWVhcmx5QW5vbSR5ZWFyIDwtIElTT2RhdGUoeWVhcmx5QW5vbSR5ZWFyLCAwMSwgMDEsIGhvdXIgPSAwKQpnZ3Bsb3QoeWVhcmx5QW5vbSwgYWVzKHllYXIsIGNobFlyQW5vbSkpICsgZ2VvbV9saW5lKCkgKyAKICB0aGVtZV9idygpICsgZ2d0aXRsZSgneWVhcmx5IGNobGEgYW5vbScpCmBgYCAKCmBgYHtyIGNhbENPRklQbG90cHAsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSAzLCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy5zaG93ID0gJ2hvbGQnLCB3YXJuaW5nID0gRkFMU0V9CmdncGxvdCh5ZWFybHlBbm9tLCBhZXMoeWVhciwgcHBZckFub20pKSArIGdlb21fbGluZSgpICsgCiAgdGhlbWVfYncoKSArIGdndGl0bGUoJ3llYXJseSBwcCBhbm9tJykKCmBgYAoKIyMjIENQUyBUcmF3bCBTdXJ2ZXlzCgoKVGhlIENQUyAoQ29hc3RhbCBQZWxhZ2ljIFNwZWNpZXMpIFRyYXdsIExpZmUgSGlzdG9yeSBMZW5ndGggRnJlcXVlbmN5IERhdGEgY29udGFpbnMgdGhlIGxlbmd0aCBkaXN0cmlidXRpb24gb2YgYSBzdWJzZXQgb2YgaW5kaXZpZHVhbHMgZnJvbSBhIHNwZWNpZXMgKG1haW5seSBub24tdGFyZ2V0KSBjYXVnaHQgZHVyaW5nIFNXRlNDLUZSRCBmaXNoZXJ5IGluZGVwZW5kZW50IHRyYXdsIHN1cnZleXMgb2YgY29hc3RhbCBwZWxhZ2ljIHNwZWNpZXMuIE1lYXN1cmVkIGxlbmd0aHMgZm9yIGluZGljYXRlZCBsZW5ndGggdHlwZSAoZm9yaywgc3RhbmRhcmQsIHRvdGFsLCBvciBtYW50bGUpIHdlcmUgZ3JvdXBlZCBpbiAxMCBtbSBiaW5zIChpZGVudGlmaWVkIGJ5IHRoZSBtaWRwb2ludCBvZiB0aGUgbGVuZ3RoIGNsYXNzKSBhbmQgY291bnRzIGFyZSByZWNvcmRlZCBieSBzZXguCgpXZSB3aWxsIGxvb2sgYXQgdGhlIG51bWJlciBhbmQgbG9jYXRpb24gb2Ygc2FyZGluZXMgKFNhcmRpbm9wcyBzYWdheCkgaW4gdGhlIHRvd3MgaW4gTWFyY2ggMjAxMCBhbmQgMjAxMSwgYW5kIGNvbXBhcmUgd2l0aCBtb250aGx5IFNTVCBmcm9tIHNhdGVsbGl0ZXMuICBGaXJzdCB3ZSBxdWVyeSB0aGUgPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWUiPkVSRERBUDwvc3Bhbj4gc2VydmVyIHRvIHNlZSBpZiBDUFMgVHJhd2wgZGF0YSBhcmUgYXZhaWxhYmxlIHRocm91Z2ggdGhlIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlIj5FUkREQVA8L3NwYW4+IHNlcnZlciwgYW5kIGlmIHNvLCBnZXQgdGhlIGRhdGFzZXRJRCBmb3IgdGhlIGRhdGEgd2Ugd2FudC4KCmBgYHtyIENQU2dldH0KcmVxdWlyZSgicmVyZGRhcCIpCihDUFNpbmZvIDwtIGluZm8oJ0ZSRENQU1RyYXdsTEhIYXVsQ2F0Y2gnKSkKcmVxdWlyZSgiZHBseXIiKQpyZXF1aXJlKCJyZXJkZGFwIikKc2FyZGluZXMgPC0gdGFibGVkYXAoQ1BTaW5mbywgZmllbGRzID0gYygnbGF0aXR1ZGUnLCAgJ2xvbmdpdHVkZScsICd0aW1lJywgJ3NjaWVudGlmaWNfbmFtZScsICdzdWJzYW1wbGVfY291bnQnKSwgJ3RpbWU+PTIwMTAtMDEtMDEnLCAndGltZTw9MjAxMi0wMS0wMScsICdzY2llbnRpZmljX25hbWU9IlNhcmRpbm9wcyBzYWdheCInICkKc2FyZGluZXMkdGltZSA8LSBhcy5EYXRlKHNhcmRpbmVzJHRpbWUsIG9yaWdpbiA9ICcxOTcwLTAxLTAxJywgdHogPSAiR01UIikKc2FyZGluZXMkbGF0aXR1ZGUgPC0gYXMubnVtZXJpYyhzYXJkaW5lcyRsYXRpdHVkZSkKc2FyZGluZXMkbG9uZ2l0dWRlIDwtIGFzLm51bWVyaWMoc2FyZGluZXMkbG9uZ2l0dWRlKQpzYXJkaW5lMjAxMCA8LSBmaWx0ZXIoc2FyZGluZXMsIHRpbWUgPCBhcy5EYXRlKCcyMDEwLTEyLTAxJykpCgpgYGAKCnRoZW4gd2UgZ2V0IG1vbnRobHkgTU9ESVMgU1NUIGZvciB0aG9zZSB0aW1lIHBlcmlvZHM6CgpgYGB7ciBNV3NzdGdldH0KcmVxdWlyZSgicmVyZGRhcCIpCiMgZ2V0IHRoZSBkYXRhc2V0IGluZm8Kc3N0SW5mbyA8LSBpbmZvKCdlcmRNV3NzdGRtZGF5JykKIyBnZXQgMjAxMDA0IG1vbnRobHkgc3N0CnNzdDIwMTAwNCA8LSBncmlkZGFwKCdlcmRNV3NzdGRtZGF5JywgbGF0aXR1ZGUgPSBjKDIyLiwgNTEuKSwgbG9uZ2l0dWRlID0gYygyMjAuLCAyNTUpLCB0aW1lID0gYygnMjAxMC0wNC0xNicsJzIwMTAtMDQtMTYnKSwgZmllbGRzID0gJ3NzdCcpCiMgZ2V0IDIwMTEwNCBtb250aGx5IHNzdApzc3QyMDExMDQgPC0gZ3JpZGRhcCgnZXJkTVdzc3RkbWRheScsIGxhdGl0dWRlID0gYygyMi4sIDUxLiksIGxvbmdpdHVkZSA9IGMoMjIwLiwgMjU1KSwgdGltZSA9IGMoJzIwMTEtMDQtMTYnLCcyMDExLTA0LTE2JyksIGZpZWxkcyA9ICdzc3QnKQoKYGBgCgphbmQgcGxvdCB0aGUgc2FyZGluZSBjb3VudHMgb24gdGhlIG1vbnRobHkgU1NUOgoKYGBge3IgQ1BTUGxvdCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDYsIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLnNob3cgPSAnaG9sZCcsIHdhcm5pbmcgPSBGQUxTRX0KcmVxdWlyZSgiZHBseXIiKQpyZXF1aXJlKCJnZ3Bsb3QyIikKcmVxdWlyZSgibWFwZGF0YSIpCiMgZ2V0IHBvbHlnb25zIG9mIGNvYXN0IGZvciB0aGlzIGFyZWEKdyA8LSBtYXBfZGF0YSgid29ybGRIaXJlcyIsIHlsaW0gPSBjKDIyLiwgNTEuKSwgeGxpbSA9IGMoMjIwIC0gMzYwLCAyNTAgLSAzNjApKQojIHBsb3QgMjAxMDA0IHNzdCBvbiB0aGUgbWFwCnNhcmRpbmUyMDEwIDwtIGZpbHRlcihzYXJkaW5lcywgdGltZSA8IGFzLkRhdGUoJzIwMTAtMTItMDEnLCBvcmlnaW4gPSAnMTk3MC0wMS0wMScsIHR6ID0gIkdNVCIpKQpzYXJkaW5lMjAxMSA8LSBmaWx0ZXIoc2FyZGluZXMsIHRpbWUgPiBhcy5EYXRlKCcyMDEwLTEyLTAxJywgb3JpZ2luID0gJzE5NzAtMDEtMDEnLCB0eiA9ICJHTVQiKSkKbXljb2xvciA8LSBjb2xvcnMkdGVtcGVyYXR1cmUKcDEgPC0gZ2dwbG90KCkgKyAKICBnZW9tX3BvbHlnb24oZGF0YSA9IHcsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGZpbGwgPSAiZ3JleTgwIikgKwogIGdlb21fcmFzdGVyKGRhdGEgPSBzc3QyMDEwMDQkZGF0YSwgYWVzKHggPSBsb24gLSAzNjAsIHkgPSBsYXQsIGZpbGwgPSBzc3QpLCBpbnRlcnBvbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IG15Y29sb3IsIG5hLnZhbHVlID0gTkEsIGxpbWl0cyA9IGMoNSwzMCkpICsKICB0aGVtZV9idygpICsgeWxhYigibGF0aXR1ZGUiKSArIHhsYWIoImxvbmdpdHVkZSIpICsKICBjb29yZF9maXhlZCgxLjMsIHhsaW0gPSBjKDIyMCAtIDM2MCwgMjUwIC0gMzYwKSwgIHlsaW0gPSBjKDIyLiwgNTEuKSkKCiMgcGxvdCAyMDExMDQgc3N0IG9uIHRoZSBtYXAKcDIgPC0gZ2dwbG90KCkgKyAKICBnZW9tX3BvbHlnb24oZGF0YSA9IHcsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGZpbGwgPSAiZ3JleTgwIikgKwogIGdlb21fcmFzdGVyKGRhdGEgPSBzc3QyMDExMDQkZGF0YSwgYWVzKHggPSBsb24gLSAzNjAsIHkgPSBsYXQsIGZpbGwgPSBzc3QpLCBpbnRlcnBvbGF0ZSA9IEZBTFNFKSArCiAgZ2VvbV9wb2ludChkYXRhID0gc2FyZGluZTIwMTEsIGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUsIGNvbG91ciA9IHN1YnNhbXBsZV9jb3VudCkpICsKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gbXljb2xvciwgbmEudmFsdWUgPSBOQSwgbGltaXRzID0gYyg1LDMwKSkgKwogIHRoZW1lX2J3KCkgKyB5bGFiKCJsYXRpdHVkZSIpICsgeGxhYigibG9uZ2l0dWRlIikgKwogIGNvb3JkX2ZpeGVkKDEuMywgeGxpbSA9IGMoMjIwIC0gMzYwLCAyNTAgLSAzNjApLCAgeWxpbSA9IGMoMjIuLCA1MS4pKQpwMSArIGdlb21fcG9pbnQoZGF0YSA9IHNhcmRpbmUyMDEwLCBhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlLCBjb2xvdXIgPSBzdWJzYW1wbGVfY291bnQpKSArIHNjYWxlX2NvbG91cl9ncmFkaWVudChzcGFjZSA9ICJMYWIiLCBuYS52YWx1ZSA9IE5BLCBsaW1pdHMgPSBjKDAsODApKQoKcDIgKyAgIGdlb21fcG9pbnQoZGF0YSA9IHNhcmRpbmUyMDExLCBhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlLCBjb2xvdXIgPSBzdWJzYW1wbGVfY291bnQpKSArIHNjYWxlX2NvbG91cl9ncmFkaWVudChzcGFjZSA9ICJMYWIiLCBuYS52YWx1ZSA9IE5BLCBsaW1pdHMgPSBjKDAsODApKQoKYGBgCgpXZSBjYW4gYWxzbyBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2Ygc2FyZGluZXMgdGhyb3VnaCB0aGUgeWVhcnM6CgpgYGB7ciBzYXJkaW5lc0dldH0KcmVxdWlyZSgicmVyZGRhcCIpCnNhcmRpbm9wcyA8LSB0YWJsZWRhcChDUFNpbmZvLCBmaWVsZHMgPSBjKCdsb25naXR1ZGUnLCAnbGF0aXR1ZGUnLCAndGltZScpLCAgJ3NjaWVudGlmaWNfbmFtZT0iU2FyZGlub3BzIHNhZ2F4IicpCnNhcmRpbm9wcyR0aW1lIDwtIGFzLkRhdGUoc2FyZGlub3BzJHRpbWUsIG9yaWdpbiA9ICcxOTcwLTAxLTAxJywgdHogPSAiR01UIikKc2FyZGlub3BzJHllYXIgPC0gYXMuZmFjdG9yKGZvcm1hdChzYXJkaW5vcHMkdGltZSwgJyVZJykpCnNhcmRpbm9wcyRsYXRpdHVkZSA8LSBhcy5udW1lcmljKHNhcmRpbm9wcyRsYXRpdHVkZSkKc2FyZGlub3BzJGxvbmdpdHVkZSA8LSBhcy5udW1lcmljKHNhcmRpbm9wcyRsb25naXR1ZGUpCgpgYGAKCmFuZCBwbG90IHRoZSByZXN1bHRzLCB3aXRoIGEgZGlmZmVyZW50IGNvbG9yIGZvciBlYWNoIHllYXI6CgpgYGB7ciBzYXJkaW5lc1Bsb3QsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA1LCBmaWcuYWxpZ249J2NlbnRlcicsIHdhcm5pbmcgPSBGQUxTRX0KcmVxdWlyZSgiZ2dwbG90MiIpCnJlcXVpcmUoIm1hcGRhdGEiKQp4bGltIDwtIGMoLTEzNSwgLTExMCkKeWxpbSA8LSBjKDMwLCA1MSkKY29hc3QgPC0gbWFwX2RhdGEoIndvcmxkSGlyZXMiLCB5bGltID0geWxpbSwgeGxpbSA9IHhsaW0pCmdncGxvdCgpICsgCiAgICBnZW9tX3BvaW50KGRhdGEgPSBzYXJkaW5vcHMsIGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUsIGNvbG91ciA9IHllYXIpKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IGNvYXN0LCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCBmaWxsID0gImdyZXk4MCIpICsKICAgIHRoZW1lX2J3KCkgKyB5bGFiKCJsYXRpdHVkZSIpICsgeGxhYigibG9uZ2l0dWRlIikgKwogICAgY29vcmRfZml4ZWQoMS4zLCB4bGltID0geGxpbSwgeWxpbSA9IHlsaW0pICsKICAgIGdndGl0bGUoIkxvY2F0aW9uIG9mIHNhcmRpbmVzIGJ5IHllYXIgaW4gRVBNIFRyYXdscyIpCgpgYGAKCiMjIyBOREJDIEJ1b3lzCgpOT0FBJ3MgTmF0aW9uYWwgRGF0YSBCdW95IENlbnRlciAoTkRCQykgY29sbGVjdHMgd29ybGQtd2lkZSBkYXRhIGZyb20gYnVveXMgaW4gdGhlIG9jZWFuLiA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5FUkREQVA8L3NwYW4+IGNhbiBiZSBzZWFyY2hlZCBmb3IgdGhlIGxvY2F0aW9uIG9mIGFsbCBidW95cyBpbiBhIGJvdW5kaW5nIGJveCB3aXRoIGxhdGl0dWRlcygzN04sIDQ3TikgYW5kIGxvbmdpdHVkZXMgKDEyNFcsIDEyMVcpOiAKCmBgYHtyIE5EQkNHZXR9CiMgZ2V0IG9jYXRpb24gYW5kIHN0YXRpb24gSUQgb2YgTkRCQyBidW95cyBpbiBzYW1lIHJlZ2lvbgpyZXF1aXJlKCJnZ3Bsb3QyIikKcmVxdWlyZSgibWFwZGF0YSIpCkJ1b3lzSW5mbyA8LSBpbmZvKCdjd3djTkRCQ01ldCcpCmxvY2F0aW9uQnVveXMgPC0gdGFibGVkYXAoQnVveXNJbmZvLCBkaXN0aW5jdCA9IFRSVUUsIGZpZWxkcyA9IGMoInN0YXRpb24iLCAibG9uZ2l0dWRlIiwgImxhdGl0dWRlIiksICJsb25naXR1ZGU+PS0xMjQiLCAibG9uZ2l0dWRlPD0tMTIxIiwgImxhdGl0dWRlPj0zNyIsICJsYXRpdHVkZTw9NDciKQpsb2NhdGlvbkJ1b3lzJGxhdGl0dWRlIDwtIGFzLm51bWVyaWMobG9jYXRpb25CdW95cyRsYXRpdHVkZSkKbG9jYXRpb25CdW95cyRsb25naXR1ZGUgPC0gYXMubnVtZXJpYyhsb2NhdGlvbkJ1b3lzJGxvbmdpdHVkZSkKYGBgCgphbmQgdGhlIHJlc3VsdHMgcGxvdHRlZDoKCmBgYHtyIE5EQkNQbG90LCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNSwgZmlnLmFsaWduPSdjZW50ZXInLCB3YXJuaW5nID0gRkFMU0V9CnJlcXVpcmUoImdncGxvdDIiKQpyZXF1aXJlKCJtYXBkYXRhIikKeGxpbSA8LSBjKC0xMzAsIC0xMTApCnlsaW0gPC0gYygzNSwgNTApCmNvYXN0IDwtIG1hcF9kYXRhKCJ3b3JsZEhpcmVzIiwgeWxpbSA9IHlsaW0sIHhsaW0gPSB4bGltKQpnZ3Bsb3QoKSArIAogICBnZW9tX3BvaW50KGRhdGEgPSBsb2NhdGlvbkJ1b3lzLCBhZXMoeCA9IGxvbmdpdHVkZSAsIHkgPSBsYXRpdHVkZSwgY29sb3VyID0gZmFjdG9yKHN0YXRpb24pICkpICsgCiAgIGdlb21fcG9seWdvbihkYXRhID0gY29hc3QsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGZpbGwgPSAiZ3JleTgwIikgKwogICB0aGVtZV9idygpICsgeWxhYigibGF0aXR1ZGUiKSArIHhsYWIoImxvbmdpdHVkZSIpICsKICAgY29vcmRfZml4ZWQoMS4zLCB4bGltID0geGxpbSwgeWxpbSA9IHlsaW0pICsKICAgZ2d0aXRsZSgiTG9jYXRpb24gb2YgYnVveXMgaW4gZ2l2ZW4gcmVnaW9uIikKCmBgYAoKTG9va2luZyBhdCB3aW5kIHNwZWVkIGZvciAyMDEyIGZvciBzdGF0aW9uICI0NjAxMiIKCmBgYHtyIE5EQkNUUywgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDMsIGZpZy5hbGlnbj0nY2VudGVyJywgd2FybmluZyA9IEZBTFNFfQpyZXF1aXJlKCJyZXJkZGFwIikKYnVveURhdGEgPC0gdGFibGVkYXAoQnVveXNJbmZvLCBmaWVsZHMgPSBjKCJ0aW1lIiwgIndzcGQiKSwgJ3N0YXRpb249IjQ2MDEyIicsICd0aW1lPj0yMDEyLTAxLTAxJywgJ3RpbWU8PTIwMTMtMDEtMDEnKQpidW95RGF0YSR3c3BkIDwtIGFzLm51bWVyaWMoYnVveURhdGEkd3NwZCkKYnVveURhdGEkdGltZSA8LSBhcy5EYXRlKGJ1b3lEYXRhJHRpbWUsIG9yaWdpbiA9ICcxOTcwLTAxLTAxJywgdHogPSAiR01UIikKZ2dwbG90KGJ1b3lEYXRhLCBhZXModGltZSwgd3NwZCkpICsgZ2VvbV9saW5lKCkgKyB0aGVtZV9idygpICsgeWxhYigid2luZCBzcGVlZCIpICsKICAgICAgZ2d0aXRsZSgiV2luZCBTcGVlZCBpbiAyMDEyIGZyb20gYnVveSA0NjAxMiAiKQoKYGBgCgoKIyMjICBJT09TIEdsaWRlciBEYXRhCgpUaGUgbWlzc2lvbiBvZiB0aGUgSU9PUyBHbGlkZXIgREFDIGlzIHRvIHByb3ZpZGUgZ2xpZGVyIG9wZXJhdG9ycyB3aXRoIGEgc2ltcGxlIHByb2Nlc3MgZm9yIHN1Ym1pdHRpbmcgZ2xpZGVyIGRhdGEgc2V0cyB0byBhIGNlbnRyYWxpemVkIGxvY2F0aW9uLCBlbmFibGluZyB0aGUgZGF0YSB0byBiZSB2aXN1YWxpemVkLCBhbmFseXplZCwgd2lkZWx5IGRpc3RyaWJ1dGVkIHZpYSBleGlzdGluZyB3ZWIgc2VydmljZXMgYW5kIHRoZSBHbG9iYWwgVGVsZWNvbW11bmljYXRpb25zIFN5c3RlbSAoR1RTKSBhbmQgYXJjaGl2ZWQgYXQgdGhlIE5hdGlvbmFsIENlbnRlcnMgZm9yIEVudmlyb25tZW50YWwgSW5mb3JtYXRpb24gKE5DRUkpLgpUaGUgSU9PUyBHbGlkZXIgRGFjIGlzIGFjY2Vzc2libGUgdGhyb3VnaCBgcmVyZGRhcGAgKGh0dHA6Ly9kYXRhLmlvb3MudXMvZ2xpZGVycy9lcmRkYXAvKS4gIEV4dHJhY3RpbmcgYW5kIHBsb3R0aW5nIHNhbGluaXR5IGZyb20gcGFydCBvZiB0aGUgcGF0aCBvZiBvbmUgZ2xpZGVyIGRlcGxveWVkIGJ5IHRoZSBTY3JpcHBzIEluc3RpdHV0aW9uIG9mIE9jZWFub2dyYXBoeToKCmBgYHtyIGdsaWRlckdldH0KcmVxdWlyZSgicmVyZGRhcCIpCnVybEJhc2UgPC0gImh0dHBzOi8vZGF0YS5pb29zLnVzL2dsaWRlcnMvZXJkZGFwLyIKZ2xpZGVySW5mbyA8LSBpbmZvKCJzcDA2NC0yMDE2MTIxNFQxOTEzIiwgIHVybCA9IHVybEJhc2UpCmdsaWRlciA8LSB0YWJsZWRhcChnbGlkZXJJbmZvLCBmaWVsZHMgPSBjKCJsb25naXR1ZGUiLCAibGF0aXR1ZGUiLCAiZGVwdGgiLCAic2FsaW5pdHkiKSwgJ3RpbWU+PTIwMTYtMTItMTQnLCAndGltZTw9MjAxNi0xMi0yMycsIHVybCA9IHVybEJhc2UpCmdsaWRlciRsb25naXR1ZGUgPC0gYXMubnVtZXJpYyhnbGlkZXIkbG9uZ2l0dWRlKQpnbGlkZXIkbGF0aXR1ZGUgPC0gYXMubnVtZXJpYyhnbGlkZXIkbGF0aXR1ZGUpCmdsaWRlciRkZXB0aCA8LSBhcy5udW1lcmljKGdsaWRlciRkZXB0aCkKCmBgYAoKYW5kIGRyYXcgYSAzLUQgcGxvdCBvZiB0aGUgdHJhY2s6CgpgYGB7ciBnbGlkZXJQbG90LCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNSwgZmlnLmFsaWduID0gJ2NlbnRlcicsIHdhcm5pbmcgPSBGQUxTRX0KcmVxdWlyZSgicGxvdDNEIikKc2NhdHRlcjNEKHggPSBnbGlkZXIkbG9uZ2l0dWRlICwgeSA9IGdsaWRlciRsYXRpdHVkZSAsIHogPSAtZ2xpZGVyJGRlcHRoLCBjb2x2YXIgPSBnbGlkZXIkc2FsaW5pdHksICAgICAgICAgICAgICBjb2wgPSBjb2xvcnMkc2FsaW5pdHksIHBoaSA9IDQwLCB0aGV0YSA9IDI1LCBidHkgPSAiZyIsIHR5cGUgPSAicCIsIAogICAgICAgICAgIHRpY2t0eXBlID0gImRldGFpbGVkIiwgcGNoID0gMTAsIGNsaW0gPSBjKDMzLjIsMzQuMzEpLCBjbGFiID0gJ1NhbGluaXR5JywgCiAgICAgICAgICAgeGxhYiA9ICJsb25naXR1ZGUiLCB5bGFiID0gImxhdGl0dWRlIiwgemxhYiA9ICJkZXB0aCIsCiAgICAgICAgICAgY2V4ID0gYygwLjUsIDEsIDEuNSkpCmBgYAo=