Adapted from a blog post by Sam Tyner

library(dplyr)
library(tidyr)
##library(rnoaa)
library(ggplot2)
library(ggrepel)
library(patchwork)
theme_set(theme_minimal() + theme(text = element_text(size = 16)))

Getting the Data from NOAA

Climate data is available via a web service API from NOAA.

The rnoaa package provides an interface to this API. A tutorial is available from rOpenSci.

This code would be used to find the most suitable Iowa City station:

options("<key>") ## may not be needed
stat <- ghcnd_stations()
ic_stat <- filter(stat, grepl("IOWA CITY", name) & element == "SNOW")
select(ic_stat, name, id, ends_with("year"))

The best station seems to be

ic_id <- "USC00134101"

The current data for this station can then be obtained from NOAA:

ic_data_raw <- ghcnd(ic_id, refresh = TRUE)
## goes into a cache file as csv
## /home/luke/.cache/rnoaa/ghcnd/USC00134101.dly
write.csv(ic_data_raw, row.names = FALSE, file = "ic_noaa.csv")
## gzip and move to data directory

I have done these steps and stored the data locally; I will update the data as the course progresses.

Download the local data copy:

if (! file.exists("ic_noaa.csv.gz"))
    download.file("http://www.stat.uiowa.edu/~luke/data/ic_noaa.csv.gz",
                  "ic_noaa.csv.gz")
## make sure data is up to date
ic_data_raw <- as_tibble(read.csv("ic_noaa.csv.gz", head = TRUE))

Processing the Data

The raw data:

ic_data_raw
## # A tibble: 10,133 × 128
##    id        year month element VALUE1 MFLAG1 QFLAG1 SFLAG1 VALUE2 MFLAG2 QFLAG2
##    <chr>    <int> <int> <chr>    <int> <chr>  <chr>  <chr>   <int> <chr>  <chr> 
##  1 USC0013…  1893     1 TMAX       -17 " "    " "    6         -28 " "    " "   
##  2 USC0013…  1893     1 TMIN       -67 " "    " "    6        -161 " "    " "   
##  3 USC0013…  1893     1 PRCP         0 "P"    " "    6           0 "P"    " "   
##  4 USC0013…  1893     1 SNOW         0 " "    " "    6           0 " "    " "   
##  5 USC0013…  1893     2 TMAX        11 " "    " "    6         -56 " "    " "   
##  6 USC0013…  1893     2 TMIN      -233 " "    " "    6        -206 " "    " "   
##  7 USC0013…  1893     2 PRCP         0 "P"    " "    6         102 " "    " "   
##  8 USC0013…  1893     2 SNOW         0 " "    " "    6          20 " "    " "   
##  9 USC0013…  1893     3 TMAX        56 " "    " "    6          44 " "    " "   
## 10 USC0013…  1893     3 TMIN       -78 " "    " "    6         -67 " "    " "   
## # ℹ 10,123 more rows
## # ℹ 117 more variables: SFLAG2 <chr>, VALUE3 <int>, MFLAG3 <chr>, QFLAG3 <chr>,
## #   SFLAG3 <chr>, VALUE4 <int>, MFLAG4 <chr>, QFLAG4 <chr>, SFLAG4 <chr>,
## #   VALUE5 <int>, MFLAG5 <chr>, QFLAG5 <chr>, SFLAG5 <chr>, VALUE6 <int>,
## #   MFLAG6 <chr>, QFLAG6 <chr>, SFLAG6 <chr>, VALUE7 <int>, MFLAG7 <chr>,
## #   QFLAG7 <chr>, SFLAG7 <chr>, VALUE8 <int>, MFLAG8 <chr>, QFLAG8 <chr>,
## #   SFLAG8 <chr>, VALUE9 <int>, MFLAG9 <chr>, QFLAG9 <chr>, SFLAG9 <chr>, …

The documentation for the data is available at

https://www.ncei.noaa.gov/pub/data/ghcn/daily/readme.txt

The available elements, or measurements, are:

unique(ic_data_raw$element)
##  [1] "TMAX" "TMIN" "PRCP" "SNOW" "WT16" "WT01" "TOBS" "SNWD" "WT18" "WT03"
## [11] "WT04" "WT14" "EVAP" "WDMV" "WT05" "DAEV" "MDEV" "DAWM" "MDWM" "DAPR"
## [21] "MDPR" "MNPN" "MXPN" "WESD"

The primary ones are:

PRCP = Precipitation (tenths of mm)
SNOW = Snowfall (mm)
SNWD = Snow depth (mm)
TMAX = Maximum temperature (tenths of degrees C)
TMIN = Minimum temperature (tenths of degrees C)

The values for day \(k\) are stored in the variable VALUE\(k\). A selection:

ic_feb2019 <- filter(ic_data_raw, year == 2019, month == 2)
select(ic_feb2019, element, VALUE1, VALUE28, VALUE31)
## # A tibble: 6 × 4
##   element VALUE1 VALUE28 VALUE31
##   <chr>    <int>   <int>   <int>
## 1 TMAX      -167     -72      NA
## 2 TMIN      -344    -139      NA
## 3 TOBS      -194    -128      NA
## 4 PRCP        18       0      NA
## 5 SNOW        51       0      NA
## 6 SNWD       330      51      NA

We need to:

Start by selecting the columns we need:

ic_data <- select(ic_data_raw, year, month, element, starts_with("VALUE"))
ic_data
## # A tibble: 10,133 × 34
##     year month element VALUE1 VALUE2 VALUE3 VALUE4 VALUE5 VALUE6 VALUE7 VALUE8
##    <int> <int> <chr>    <int>  <int>  <int>  <int>  <int>  <int>  <int>  <int>
##  1  1893     1 TMAX       -17    -28   -150    -44    -17   -106    -56    -67
##  2  1893     1 TMIN       -67   -161   -233   -156   -156   -206   -111   -211
##  3  1893     1 PRCP         0      0      0     64      0     38      0      0
##  4  1893     1 SNOW         0      0      0     64      0     38      0      0
##  5  1893     2 TMAX        11    -56   -106   -122     33     28   -161    -94
##  6  1893     2 TMIN      -233   -206   -217   -267   -122   -211   -256   -278
##  7  1893     2 PRCP         0    102      0      0      0    102      0      0
##  8  1893     2 SNOW         0     20      0      0      0    102      0      0
##  9  1893     3 TMAX        56     44     17    -28     17     61     83     72
## 10  1893     3 TMIN       -78    -67   -122   -156   -122   -106    -28     17
## # ℹ 10,123 more rows
## # ℹ 23 more variables: VALUE9 <int>, VALUE10 <int>, VALUE11 <int>,
## #   VALUE12 <int>, VALUE13 <int>, VALUE14 <int>, VALUE15 <int>, VALUE16 <int>,
## #   VALUE17 <int>, VALUE18 <int>, VALUE19 <int>, VALUE20 <int>, VALUE21 <int>,
## #   VALUE22 <int>, VALUE23 <int>, VALUE24 <int>, VALUE25 <int>, VALUE26 <int>,
## #   VALUE27 <int>, VALUE28 <int>, VALUE29 <int>, VALUE30 <int>, VALUE31 <int>

Reshape from (very) wide to (too) long:

ic_data <- pivot_longer(ic_data, VALUE1 : VALUE31,
                        names_to = "day", values_to = "value")
ic_data
## # A tibble: 314,123 × 5
##     year month element day     value
##    <int> <int> <chr>   <chr>   <int>
##  1  1893     1 TMAX    VALUE1    -17
##  2  1893     1 TMAX    VALUE2    -28
##  3  1893     1 TMAX    VALUE3   -150
##  4  1893     1 TMAX    VALUE4    -44
##  5  1893     1 TMAX    VALUE5    -17
##  6  1893     1 TMAX    VALUE6   -106
##  7  1893     1 TMAX    VALUE7    -56
##  8  1893     1 TMAX    VALUE8    -67
##  9  1893     1 TMAX    VALUE9     11
## 10  1893     1 TMAX    VALUE10  -150
## # ℹ 314,113 more rows

Extract the day as a number:

ic_data <- mutate(ic_data, day = readr::parse_number(day))
ic_data
## # A tibble: 314,123 × 5
##     year month element   day value
##    <int> <int> <chr>   <dbl> <int>
##  1  1893     1 TMAX        1   -17
##  2  1893     1 TMAX        2   -28
##  3  1893     1 TMAX        3  -150
##  4  1893     1 TMAX        4   -44
##  5  1893     1 TMAX        5   -17
##  6  1893     1 TMAX        6  -106
##  7  1893     1 TMAX        7   -56
##  8  1893     1 TMAX        8   -67
##  9  1893     1 TMAX        9    11
## 10  1893     1 TMAX       10  -150
## # ℹ 314,113 more rows

Reshape from too long to tidy with one row per day, keeping only the primary variables:

corevars <- c("TMAX", "TMIN", "PRCP", "SNOW", "SNWD")
ic_data <- filter(ic_data, element %in% corevars)
ic_data <- pivot_wider(ic_data, names_from = "element", values_from = "value")
ic_data
## # A tibble: 47,120 × 8
##     year month   day  TMAX  TMIN  PRCP  SNOW  SNWD
##    <int> <int> <dbl> <int> <int> <int> <int> <int>
##  1  1893     1     1   -17   -67     0     0    NA
##  2  1893     1     2   -28  -161     0     0    NA
##  3  1893     1     3  -150  -233     0     0    NA
##  4  1893     1     4   -44  -156    64    64    NA
##  5  1893     1     5   -17  -156     0     0    NA
##  6  1893     1     6  -106  -206    38    38    NA
##  7  1893     1     7   -56  -111     0     0    NA
##  8  1893     1     8   -67  -211     0     0    NA
##  9  1893     1     9    11  -144     0     0    NA
## 10  1893     1    10  -150  -250     0     0    NA
## # ℹ 47,110 more rows

Add a date variable for plotting and to help get rid of bogus days:

ic_data <- mutate(ic_data, date = lubridate::make_date(year, month, day))
filter(ic_data, year == 2019, month == 2, day >= 27)
## # A tibble: 5 × 9
##    year month   day  TMAX  TMIN  PRCP  SNOW  SNWD date      
##   <int> <int> <dbl> <int> <int> <int> <int> <int> <date>    
## 1  2019     2    27   -44  -128     0     0    51 2019-02-27
## 2  2019     2    28   -72  -139     0     0    51 2019-02-28
## 3  2019     2    29    NA    NA    NA    NA    NA NA        
## 4  2019     2    30    NA    NA    NA    NA    NA NA        
## 5  2019     2    31    NA    NA    NA    NA    NA NA

Get rid of the bogus days:

ic_data <- filter(ic_data, ! is.na(date))

Make units more standard (American):

mm2in <- function(x) x / 25.4
C2F <- function(x) 32 + 1.8 * x
ic_data <- transmute(ic_data, year, month, day, date,
                     PRCP = mm2in(PRCP / 10),
                     SNOW = mm2in(SNOW),
                     SNWD = mm2in(SNWD),
                     TMIN = C2F(TMIN / 10),
                     TMAX = C2F(TMAX / 10))

The Winters of 2018/2019 and 2020/2021

Daily Snowfall

Extract the data for days between November 1 and March 31:

w18 <- filter(ic_data, date >= "2018-11-01" & date <= "2019-03-31")
w20 <- filter(ic_data, date >= "2020-11-01" & date <= "2021-03-31")

A first set of plots:

p1 <- ggplot(w18, aes(x = date)) + geom_col(aes(y = SNOW))
p2 <- ggplot(w18, aes(x = date)) + geom_line(aes(y = SNWD))
p1 + p2

For daily snowfall it is probably reasonable to replace missing values by zeros.

w18 <- mutate(w18, SNOW = ifelse(is.na(SNOW), 0, SNOW))
w20 <- mutate(w20, SNOW = ifelse(is.na(SNOW), 0, SNOW))

For snow depth it may make sense to fill forward, replacing misusing values by the previous non-missing value. Using the fill function from tidyr is one way to do this.

w18 <- fill(w18, SNWD)
w20 <- fill(w20, SNWD)

The plot of snow depth looks a bit better with this change; the axis range for the plot of daily snowfall is also affected. Also, the warnings are eliminated.

p1 <- ggplot(w18, aes(x = date)) + geom_col(aes(y = SNOW))
p2 <- ggplot(w18, aes(x = date)) + geom_line(aes(y = SNWD))
(p1 + labs(title = "2018/19")) + p2

(p1 %+% w20 + labs(title = "2020/21")) + (p2 %+% w20)
## Warning: <ggplot> %+% x was deprecated in ggplot2 4.0.0.
## ℹ Please use <ggplot> + x instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Often snowfall is visualized in terms of the cumulative snowfall for the season up to each date:

p <- ggplot(w18, aes(x = date)) + geom_line(aes(y = cumsum(SNOW)))
p + labs(title = "2018/19")

p %+% w20 + labs(title = "2020/21")

Comparing to Long Term Averages

Compute the average snowfall for each day of the year and merge the results for the days in w18 into w18 with a left_join:

ic_avg <- group_by(ic_data, month, day) |>
    summarize(av_snow = mean(SNOW, na.rm = TRUE),
              av_snwd = mean(SNWD, na.rm = TRUE)) |>
    ungroup()
w18 <- left_join(w18, ic_avg, c("month", "day"))
w20 <- left_join(w20, ic_avg, c("month", "day"))

Daily snowfall and snow cover for the 2018/19 winter along with averages over all years in the record:

p1 <- ggplot(w18, aes(x = date)) +
    geom_col(aes(y = SNOW)) +
    geom_line(aes(y = av_snow), color = "blue")
p2 <- ggplot(w18, aes(x = date)) +
    geom_line(aes(y = SNWD)) +
    geom_line(aes(y = av_snwd), color = "blue")
(p1 + labs(title = "2018/19")) + p2

(p1 %+% w20 + labs(title = "2020/21")) + (p2 %+% w20)

Cumulative snowfall for the 2018/19 winter along with average cumulative snowfall over all years in the record:

p <- ggplot(w18, aes(x = date)) +
    geom_line(aes(y = cumsum(SNOW))) +
    geom_line(aes(y = cumsum(av_snow)), color = "blue")
p + labs(title = "2018/19")

p %+% w20 + labs(title = "2020/21")

A Combined Graph

w18day <- transmute(w18, date, snow = SNOW,
                    base = "2018/19",
                    type = "Daily Snowfall")
nrmday <- transmute(w18, date, snow = av_snow,
                    base = "Normal",
                    type = "Daily Snowfall")
w18cum <- transmute(w18day, date, snow = cumsum(snow),
                    base = "2018/19",
                    type = "Cumulative Snowfall")
nrmcum <- transmute(nrmday, date, snow = cumsum(snow),
                    base = "Normal",
                    type = "Cumulative Snowfall")
ggplot(w18, aes(x = date, y = snow, color = base)) +
    geom_line(data = w18cum) +
    geom_line(data = nrmcum) +
    geom_segment(aes(x = date, xend = date, y = 0, yend = snow),
                 size = 1, data = w18day) +
    geom_line(data = nrmday) +
    facet_wrap(~ type, ncol = 1, scales = "free_y") +
    xlab(element_blank()) + ylab("Snowfall (inches)") +
    ggtitle("2018/19 Snowfall in Iowa City") +
    scale_color_manual(values = c("2018/19" = "navy",
                                  "Normal" = "forestgreen")) +
    guides(color = guide_legend(override.aes = list(size = 1.5))) +
    theme_bw() +
    theme(legend.position = "bottom", legend.title = element_blank(),
          plot.title = element_text(face = "bold", hjust = .5),
          aspect.ratio = 0.4)
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: `label` cannot be a <ggplot2::element_blank> object.

Total Snow Fall by Year and Month

It is helpful to define a variable that shifts the year to include a full winter, not the end of one and the start of the next.

What month should be start with? Here are the counts for months with recorded snowfall:

count(filter(ic_data, SNOW > 0), month)
## # A tibble: 9 × 2
##   month     n
##   <int> <int>
## 1     1   559
## 2     2   456
## 3     3   300
## 4     4    73
## 5     5     1
## 6     7     1
## 7    10    20
## 8    11   136
## 9    12   477

The July snowfall seems odd:

filter(ic_data, SNOW > 0, month == 7)
## # A tibble: 1 × 9
##    year month   day date        PRCP  SNOW  SNWD  TMIN  TMAX
##   <int> <int> <dbl> <date>     <dbl> <dbl> <dbl> <dbl> <dbl>
## 1  1896     7    26 1896-07-26 0.110  2.91    NA    68  91.9

This is probably a recording error.

For October, the years with recorded recorded snowfall are shown by

count(filter(ic_data, SNOW > 0, month == 10), year)
## # A tibble: 14 × 2
##     year     n
##    <int> <int>
##  1  1898     1
##  2  1906     1
##  3  1913     2
##  4  1917     3
##  5  1923     1
##  6  1925     1
##  7  1929     1
##  8  1930     1
##  9  1967     1
## 10  1972     1
## 11  1980     2
## 12  1997     2
## 13  2019     2
## 14  2020     1

It seems reasonable to start the winter year in October:

ic_data <- mutate(ic_data, wyear = ifelse(month >= 10, year, year - 1))

The yearly total snowfall values:

tot <- group_by(ic_data, wyear) |>
    summarize(tsnow = sum(SNOW, na.rm = TRUE))
tot18 <- filter(tot, wyear == 2018)
ggplot(tot, aes(x = wyear, y = tsnow)) +
    geom_line() +
    geom_point(data = tot18, color = "red") +
    geom_text_repel(data = tot18, label = "2018/19", color = "red")

So 2018/19 is not a record, but still pretty high:

tot100 <- mutate(filter(tot, wyear >= 1918), rank = min_rank(desc(tsnow)))
filter(tot100, wyear == 2018)
## # A tibble: 1 × 3
##   wyear tsnow  rank
##   <dbl> <dbl> <int>
## 1  2018  40.8    11

What are the months with the highest average total snowfall?

mtot <- group_by(ic_data, year, wyear, month) |>
    summarize(msnow = sum(SNOW, na.rm = TRUE)) |>
    ungroup()
## `summarise()` has grouped output by 'year', 'wyear'. You can override using the
## `.groups` argument.
group_by(mtot, month) |> summarize(av_msnow = mean(msnow))
## # A tibble: 12 × 2
##    month av_msnow
##    <int>    <dbl>
##  1     1  7.41   
##  2     2  6.54   
##  3     3  4.22   
##  4     4  1.03   
##  5     5  0.00153
##  6     6  0      
##  7     7  0.0228 
##  8     8  0      
##  9     9  0      
## 10    10  0.256  
## 11    11  1.54   
## 12    12  6.27

For 2018/19:

filter(mtot, wyear == 2018)
## # A tibble: 12 × 4
##     year wyear month  msnow
##    <int> <dbl> <int>  <dbl>
##  1  2018  2018    10  0    
##  2  2018  2018    11  8.98 
##  3  2018  2018    12  0    
##  4  2019  2018     1 17.1  
##  5  2019  2018     2 14.2  
##  6  2019  2018     3  0.512
##  7  2019  2018     4  0    
##  8  2019  2018     5  0    
##  9  2019  2018     6  0    
## 10  2019  2018     7  0    
## 11  2019  2018     8  0    
## 12  2019  2018     9  0

For 2020/21:

filter(mtot, wyear == 2020)
## # A tibble: 8 × 4
##    year wyear month msnow
##   <int> <dbl> <int> <dbl>
## 1  2020  2020    10  1.50
## 2  2020  2020    11  0   
## 3  2020  2020    12  9.96
## 4  2021  2020     1 14.0 
## 5  2021  2020     2  9.69
## 6  2021  2020     3  0   
## 7  2021  2020     4  0   
## 8  2021  2020     5  0

A Data Processing Functions

If you want to look at data for another city you would need to go through the same steps starting with a new raw data frame.

Defining a function that encapsulates the steps makes this easy:

processData <- function(data_raw) {
    ## select just the columns we need:
    data <- select(data_raw, year, month, element, starts_with("VALUE"))

    ## reshape from (very) wide to (too) long
    data <- pivot_longer(data, VALUE1 : VALUE31,
                         names_to = "day", values_to = "value")
    ## extract the day as a number:
    data <- mutate(data, day = readr::parse_number(day))

    ## reshape from too long to tidy with one row per day, keeping
    ## only the primary variables:
    corevars <- c("TMAX", "TMIN", "PRCP", "SNOW", "SNWD")
    data <- filter(data, element %in% corevars)
    data <- pivot_wider(data, names_from = "element", values_from = "value")

    ## add a 'date' variable and get rid of bogus days
    data <- mutate(data, date = lubridate::make_date(year, month, day))
    data <- filter(data, ! is.na(date))

    ## make units more standard (American)
    mm2in <- function(x) x / 25.4
    C2F <- function(x) 32 + 1.8 * x
    data <- transmute(data, year, month, day, date,
                      PRCP = mm2in(PRCP / 10),
                      SNOW = mm2in(SNOW),
                      SNWD = mm2in(SNWD),
                      TMIN = C2F(TMIN / 10),
                      TMAX = C2F(TMAX / 10))

    ## add winter year variable 'wyear' starting in October
    data <- mutate(data, wyear = ifelse(month >= 10, year, year - 1))

    data
}

A quick sanity check:

stopifnot(identical(ic_data, processData(ic_data_raw)))

Often it is a good idea to include a sanity check like this in a code chunk marked as include = FALSE.

Other Explorations

pivot_longer(w18, TMIN | TMAX, names_to = "which", values_to = "value") |>
    ggplot(aes(x = date)) +
    geom_line(aes(y = value, color = which))

wd18 <- filter(ic_data, date >= "2018-10-01")
avs <- group_by(ic_data, month, day) |>
    summarize(avsnow = mean(SNOW, na.rm = TRUE),
              avsnwd = mean(SNWD, na.rm = TRUE),
              avtmin = mean(TMIN, na.rm = TRUE),
              avtmax = mean(TMAX, na.rm = TRUE)) |>
    ungroup()
## `summarise()` has grouped output by 'month'. You can override using the
## `.groups` argument.
wd18 <- left_join(wd18, avs)
## Joining with `by = join_by(month, day)`

ggplot(filter(w18, ! is.na(SNWD))) +
    geom_line(aes(x = date, y = SNWD)) +
    geom_line(aes(x = date, y = av_snwd), col = "blue")

plot(SNOW ~ date, data = wd18, type = "h")
lines(wd18$date, wd18$avsnow, col = "red")
lines(smooth.spline(wd18$date, wd18$avsnow), col = "blue")


plot(SNWD ~ date, data = fill(wd18, SNWD), type = "l")
lines(wd18$date, wd18$avsnwd, col = "red")


plot(cumsum(ifelse(is.na(SNOW), 0, SNOW)) ~ date, data = wd18, type = "l")
lines(wd18$date, cumsum(wd18$avsnow), col = "red")

thm <- theme_bw() +
    theme(panel.grid.major.x = element_blank(),
          panel.grid.minor = element_blank())

ggplot(wd18) +
    geom_col(aes(x = date, y = SNOW)) +
    geom_line(aes(x = date, y = avsnow), col = "red") +
    geom_smooth(aes(x = date, y = avsnow), se = FALSE) +
    thm
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'
## Warning: Removed 25 rows containing missing values or values outside the scale range
## (`geom_col()`).


ggplot(fill(wd18, SNWD)) +
    geom_line(aes(x = date, y = SNWD)) +
    geom_line(aes(x = date, y = avsnwd), col = "red") +
    geom_smooth(aes(x = date, y = avsnwd), se = FALSE) +
    thm
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'


ggplot(wd18) +
    geom_line(aes(x = date, y = cumsum(ifelse(is.na(SNOW), 0, SNOW)))) +
    geom_line(aes(x = date, y = cumsum(wd18$avsnow)), col = "red") +
    thm
## Warning: Use of `wd18$avsnow` is discouraged.
## ℹ Use `avsnow` instead.

ggplot(filter(ic_data, year == 2019), aes(x = date)) +
    geom_line(aes(y = TMAX, color = "TMAX")) +
    geom_line(aes(y = TMIN, color = "TMIN")) +
    thm

d13 <- filter(ic_data, wyear >= 2013, month <= 4 | month >= 10)
d13 <- mutate(d13, wday = as.numeric(date - lubridate::make_date(wyear, 10, 1)),
              SNOW = ifelse(is.na(SNOW), 0, SNOW))
ggplot(d13) +
    geom_col(aes(x = wday, y = SNOW), width = 3, position = "identity") +
    facet_wrap(~ wyear) + thm

ggplot(d13) + geom_line(aes(x = wday, y = SNOW)) + facet_wrap(~ wyear) + thm

d13 <- group_by(d13, wyear) |>
    mutate(snow_total = cumsum(SNOW)) |>
    ungroup()
ggplot(d13) +
    geom_line(aes(x = wday, y = snow_total, color = factor(wyear))) + thm

ggplot(d13) +
    geom_line(aes(x = wday, y = snow_total)) +
    facet_wrap(~ wyear) + thm

d13 <- fill(d13, SNWD)
ggplot(d13) + geom_line(aes(x = wday, y = SNWD)) + facet_wrap(~ wyear) + thm

w <- filter(ic_data, wyear >= 2013)
w <- mutate(w, wday = date - lubridate::make_date(wyear, 10, 1))
w <- filter(w, wday <= 180)
w <- mutate(w, wday = lubridate::make_date(2018, 10, 1) + wday)
ggplot(w) +
    geom_line(aes(x = wday, y = TMAX), color = "blue") +
    geom_line(aes(x = wday, y = TMIN), color = "red") +
    facet_wrap(~ wyear)

LS0tCnRpdGxlOiAiU25vd2ZhbGwgaW4gSW93YSBDaXR5IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6ICJzaG93IgotLS0KCkFkYXB0ZWQgZnJvbSBhIFtibG9nCnBvc3RdKGh0dHBzOi8vc2N0eW5lci5naXRodWIuaW8vcmVkb2luZy1ncmFwaHMuaHRtbCkgYnkgW1NhbQpUeW5lcl0oaHR0cHM6Ly9zY3R5bmVyLmdpdGh1Yi5pbykKCmBgYHtyIGdsb2JhbF9vcHRpb25zLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChjb2xsYXBzZSA9IFRSVUUsIGZpZy5hbGlnbiA9ICJjZW50ZXIiKQpgYGAKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCiMjbGlicmFyeShybm9hYSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkocGF0Y2h3b3JrKQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpKSkKYGBgCgojIyBHZXR0aW5nIHRoZSBEYXRhIGZyb20gTk9BQQoKQ2xpbWF0ZSBkYXRhIGlzIGF2YWlsYWJsZSB2aWEgYSBbd2ViIHNlcnZpY2UKQVBJXShodHRwczovL3d3dy5uY2RjLm5vYWEuZ292L2Nkby13ZWIvd2Vic2VydmljZXMvdjIpIGZyb20KW05PQUFdKGh0dHBzOi8vd3d3Lm5vYWEuZ292LykuCgpUaGUKW2Bybm9hYWBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9cm5vYWEpCnBhY2thZ2UgcHJvdmlkZXMgYW4gaW50ZXJmYWNlIHRvIHRoaXMgQVBJLiAgQQpbdHV0b3JpYWxdKGh0dHBzOi8vZG9jcy5yb3BlbnNjaS5vcmcvcm5vYWEvYXJ0aWNsZXMvcm5vYWEuaHRtbCkgaXMKYXZhaWxhYmxlIGZyb20gW3JPcGVuU2NpXShodHRwczovL3JvcGVuc2NpLm9yZykuCgpUaGlzIGNvZGUgd291bGQgYmUgdXNlZCB0byBmaW5kIHRoZSBtb3N0IHN1aXRhYmxlIElvd2EgQ2l0eSBzdGF0aW9uOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0Kb3B0aW9ucygiPGtleT4iKSAjIyBtYXkgbm90IGJlIG5lZWRlZApzdGF0IDwtIGdoY25kX3N0YXRpb25zKCkKaWNfc3RhdCA8LSBmaWx0ZXIoc3RhdCwgZ3JlcGwoIklPV0EgQ0lUWSIsIG5hbWUpICYgZWxlbWVudCA9PSAiU05PVyIpCnNlbGVjdChpY19zdGF0LCBuYW1lLCBpZCwgZW5kc193aXRoKCJ5ZWFyIikpCmBgYAoKVGhlIGJlc3Qgc3RhdGlvbiBzZWVtcyB0byBiZQoKYGBge3J9CmljX2lkIDwtICJVU0MwMDEzNDEwMSIKYGBgCgpUaGUgY3VycmVudCBkYXRhIGZvciB0aGlzIHN0YXRpb24gY2FuIHRoZW4gYmUgb2J0YWluZWQgZnJvbSBOT0FBOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KaWNfZGF0YV9yYXcgPC0gZ2hjbmQoaWNfaWQsIHJlZnJlc2ggPSBUUlVFKQojIyBnb2VzIGludG8gYSBjYWNoZSBmaWxlIGFzIGNzdgojIyAvaG9tZS9sdWtlLy5jYWNoZS9ybm9hYS9naGNuZC9VU0MwMDEzNDEwMS5kbHkKd3JpdGUuY3N2KGljX2RhdGFfcmF3LCByb3cubmFtZXMgPSBGQUxTRSwgZmlsZSA9ICJpY19ub2FhLmNzdiIpCiMjIGd6aXAgYW5kIG1vdmUgdG8gZGF0YSBkaXJlY3RvcnkKYGBgCgpJIGhhdmUgZG9uZSB0aGVzZSBzdGVwcyBhbmQgc3RvcmVkIHRoZSBkYXRhIGxvY2FsbHk7IEkgd2lsbCB1cGRhdGUKdGhlIGRhdGEgYXMgdGhlIGNvdXJzZSBwcm9ncmVzc2VzLgoKRG93bmxvYWQgdGhlIGxvY2FsIGRhdGEgY29weToKCmBgYHtyfQppZiAoISBmaWxlLmV4aXN0cygiaWNfbm9hYS5jc3YuZ3oiKSkKICAgIGRvd25sb2FkLmZpbGUoImh0dHA6Ly93d3cuc3RhdC51aW93YS5lZHUvfmx1a2UvZGF0YS9pY19ub2FhLmNzdi5neiIsCiAgICAgICAgICAgICAgICAgICJpY19ub2FhLmNzdi5neiIpCiMjIG1ha2Ugc3VyZSBkYXRhIGlzIHVwIHRvIGRhdGUKaWNfZGF0YV9yYXcgPC0gYXNfdGliYmxlKHJlYWQuY3N2KCJpY19ub2FhLmNzdi5neiIsIGhlYWQgPSBUUlVFKSkKYGBgCgoKIyMgUHJvY2Vzc2luZyB0aGUgRGF0YQoKVGhlIHJhdyBkYXRhOgoKYGBge3J9CmljX2RhdGFfcmF3CmBgYAoKVGhlIGRvY3VtZW50YXRpb24gZm9yIHRoZSBkYXRhIGlzIGF2YWlsYWJsZSBhdAoKPiBodHRwczovL3d3dy5uY2VpLm5vYWEuZ292L3B1Yi9kYXRhL2doY24vZGFpbHkvcmVhZG1lLnR4dAoKVGhlIGF2YWlsYWJsZSBfZWxlbWVudHNfLCBvciBtZWFzdXJlbWVudHMsIGFyZToKCmBgYHtyfQp1bmlxdWUoaWNfZGF0YV9yYXckZWxlbWVudCkKYGBgCgpUaGUgcHJpbWFyeSBvbmVzIGFyZToKCmBgYApQUkNQID0gUHJlY2lwaXRhdGlvbiAodGVudGhzIG9mIG1tKQpTTk9XID0gU25vd2ZhbGwgKG1tKQpTTldEID0gU25vdyBkZXB0aCAobW0pClRNQVggPSBNYXhpbXVtIHRlbXBlcmF0dXJlICh0ZW50aHMgb2YgZGVncmVlcyBDKQpUTUlOID0gTWluaW11bSB0ZW1wZXJhdHVyZSAodGVudGhzIG9mIGRlZ3JlZXMgQykKYGBgCgpUaGUgdmFsdWVzIGZvciBkYXkgJGskIGFyZSBzdG9yZWQgaW4gdGhlIHZhcmlhYmxlIGBWQUxVRWAkayQuIEEgc2VsZWN0aW9uOgoKYGBge3J9CmljX2ZlYjIwMTkgPC0gZmlsdGVyKGljX2RhdGFfcmF3LCB5ZWFyID09IDIwMTksIG1vbnRoID09IDIpCnNlbGVjdChpY19mZWIyMDE5LCBlbGVtZW50LCBWQUxVRTEsIFZBTFVFMjgsIFZBTFVFMzEpCmBgYAoKV2UgbmVlZCB0bzoKCi0gUmVzaGFwZSB0aGUgZGF0YS4KLSBEZWFsIHdpdGggdGhlIGJvZ3VzIGRheXMuCgpTdGFydCBieSBzZWxlY3RpbmcgdGhlIGNvbHVtbnMgd2UgbmVlZDoKCmBgYHtyfQppY19kYXRhIDwtIHNlbGVjdChpY19kYXRhX3JhdywgeWVhciwgbW9udGgsIGVsZW1lbnQsIHN0YXJ0c193aXRoKCJWQUxVRSIpKQppY19kYXRhCmBgYAoKUmVzaGFwZSBmcm9tICh2ZXJ5KSB3aWRlIHRvICh0b28pIGxvbmc6CmBgYHtyfQppY19kYXRhIDwtIHBpdm90X2xvbmdlcihpY19kYXRhLCBWQUxVRTEgOiBWQUxVRTMxLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJkYXkiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKQppY19kYXRhCmBgYAoKRXh0cmFjdCB0aGUgZGF5IGFzIGEgbnVtYmVyOgoKYGBge3J9CmljX2RhdGEgPC0gbXV0YXRlKGljX2RhdGEsIGRheSA9IHJlYWRyOjpwYXJzZV9udW1iZXIoZGF5KSkKaWNfZGF0YQpgYGAKClJlc2hhcGUgZnJvbSB0b28gbG9uZyB0byB0aWR5IHdpdGggb25lIHJvdyBwZXIgZGF5LCBrZWVwaW5nIG9ubHkgdGhlCnByaW1hcnkgdmFyaWFibGVzOgoKYGBge3J9CmNvcmV2YXJzIDwtIGMoIlRNQVgiLCAiVE1JTiIsICJQUkNQIiwgIlNOT1ciLCAiU05XRCIpCmljX2RhdGEgPC0gZmlsdGVyKGljX2RhdGEsIGVsZW1lbnQgJWluJSBjb3JldmFycykKaWNfZGF0YSA8LSBwaXZvdF93aWRlcihpY19kYXRhLCBuYW1lc19mcm9tID0gImVsZW1lbnQiLCB2YWx1ZXNfZnJvbSA9ICJ2YWx1ZSIpCmljX2RhdGEKYGBgCgpBZGQgYSBgZGF0ZWAgdmFyaWFibGUgZm9yIHBsb3R0aW5nIGFuZCB0byBoZWxwIGdldCByaWQgb2YgYm9ndXMgZGF5czoKCmBgYHtyfQppY19kYXRhIDwtIG11dGF0ZShpY19kYXRhLCBkYXRlID0gbHVicmlkYXRlOjptYWtlX2RhdGUoeWVhciwgbW9udGgsIGRheSkpCmZpbHRlcihpY19kYXRhLCB5ZWFyID09IDIwMTksIG1vbnRoID09IDIsIGRheSA+PSAyNykKYGBgCgpHZXQgcmlkIG9mIHRoZSBib2d1cyBkYXlzOgpgYGB7cn0KaWNfZGF0YSA8LSBmaWx0ZXIoaWNfZGF0YSwgISBpcy5uYShkYXRlKSkKYGBgCgpNYWtlIHVuaXRzIG1vcmUgc3RhbmRhcmQgKEFtZXJpY2FuKToKCmBgYHtyfQptbTJpbiA8LSBmdW5jdGlvbih4KSB4IC8gMjUuNApDMkYgPC0gZnVuY3Rpb24oeCkgMzIgKyAxLjggKiB4CmljX2RhdGEgPC0gdHJhbnNtdXRlKGljX2RhdGEsIHllYXIsIG1vbnRoLCBkYXksIGRhdGUsCiAgICAgICAgICAgICAgICAgICAgIFBSQ1AgPSBtbTJpbihQUkNQIC8gMTApLAogICAgICAgICAgICAgICAgICAgICBTTk9XID0gbW0yaW4oU05PVyksCiAgICAgICAgICAgICAgICAgICAgIFNOV0QgPSBtbTJpbihTTldEKSwKICAgICAgICAgICAgICAgICAgICAgVE1JTiA9IEMyRihUTUlOIC8gMTApLAogICAgICAgICAgICAgICAgICAgICBUTUFYID0gQzJGKFRNQVggLyAxMCkpCmBgYAoKCiMjIFRoZSBXaW50ZXJzIG9mIDIwMTgvMjAxOSBhbmQgMjAyMC8yMDIxCgojIyMgRGFpbHkgU25vd2ZhbGwKCkV4dHJhY3QgdGhlIGRhdGEgZm9yIGRheXMgYmV0d2VlbiBOb3ZlbWJlciAxIGFuZCBNYXJjaCAzMToKCmBgYHtyfQp3MTggPC0gZmlsdGVyKGljX2RhdGEsIGRhdGUgPj0gIjIwMTgtMTEtMDEiICYgZGF0ZSA8PSAiMjAxOS0wMy0zMSIpCncyMCA8LSBmaWx0ZXIoaWNfZGF0YSwgZGF0ZSA+PSAiMjAyMC0xMS0wMSIgJiBkYXRlIDw9ICIyMDIxLTAzLTMxIikKYGBgCgpBIGZpcnN0IHNldCBvZiBwbG90czoKCmBgYHtyLCBmaWcud2lkdGggPSA5fQpwMSA8LSBnZ3Bsb3QodzE4LCBhZXMoeCA9IGRhdGUpKSArIGdlb21fY29sKGFlcyh5ID0gU05PVykpCnAyIDwtIGdncGxvdCh3MTgsIGFlcyh4ID0gZGF0ZSkpICsgZ2VvbV9saW5lKGFlcyh5ID0gU05XRCkpCnAxICsgcDIKYGBgCgpGb3IgZGFpbHkgc25vd2ZhbGwgaXQgaXMgcHJvYmFibHkgcmVhc29uYWJsZSB0byByZXBsYWNlIG1pc3NpbmcgdmFsdWVzCmJ5IHplcm9zLgoKYGBge3J9CncxOCA8LSBtdXRhdGUodzE4LCBTTk9XID0gaWZlbHNlKGlzLm5hKFNOT1cpLCAwLCBTTk9XKSkKdzIwIDwtIG11dGF0ZSh3MjAsIFNOT1cgPSBpZmVsc2UoaXMubmEoU05PVyksIDAsIFNOT1cpKQpgYGAKCkZvciBzbm93IGRlcHRoIGl0IG1heSBtYWtlIHNlbnNlIHRvIF9maWxsIGZvcndhcmRfLCByZXBsYWNpbmcgbWlzdXNpbmcKdmFsdWVzIGJ5IHRoZSBwcmV2aW91cyBub24tbWlzc2luZyB2YWx1ZS4gVXNpbmcgdGhlIGBmaWxsYCBmdW5jdGlvbgpmcm9tIGB0aWR5cmAgaXMgb25lIHdheSB0byBkbyB0aGlzLgoKYGBge3J9CncxOCA8LSBmaWxsKHcxOCwgU05XRCkKdzIwIDwtIGZpbGwodzIwLCBTTldEKQpgYGAKClRoZSBwbG90IG9mIHNub3cgZGVwdGggbG9va3MgYSBiaXQgYmV0dGVyIHdpdGggdGhpcyBjaGFuZ2U7IHRoZSBheGlzCnJhbmdlIGZvciB0aGUgcGxvdCBvZiBkYWlseSBzbm93ZmFsbCBpcyBhbHNvIGFmZmVjdGVkLiBBbHNvLCB0aGUKd2FybmluZ3MgYXJlIGVsaW1pbmF0ZWQuCgpgYGB7ciwgZmlnLndpZHRoID0gOX0KcDEgPC0gZ2dwbG90KHcxOCwgYWVzKHggPSBkYXRlKSkgKyBnZW9tX2NvbChhZXMoeSA9IFNOT1cpKQpwMiA8LSBnZ3Bsb3QodzE4LCBhZXMoeCA9IGRhdGUpKSArIGdlb21fbGluZShhZXMoeSA9IFNOV0QpKQoocDEgKyBsYWJzKHRpdGxlID0gIjIwMTgvMTkiKSkgKyBwMgoocDEgJSslIHcyMCArIGxhYnModGl0bGUgPSAiMjAyMC8yMSIpKSArIChwMiAlKyUgdzIwKQpgYGAKCk9mdGVuIHNub3dmYWxsIGlzIHZpc3VhbGl6ZWQgaW4gdGVybXMgb2YgdGhlIGN1bXVsYXRpdmUgc25vd2ZhbGwgZm9yCnRoZSBzZWFzb24gdXAgdG8gZWFjaCBkYXRlOgoKYGBge3J9CnAgPC0gZ2dwbG90KHcxOCwgYWVzKHggPSBkYXRlKSkgKyBnZW9tX2xpbmUoYWVzKHkgPSBjdW1zdW0oU05PVykpKQpwICsgbGFicyh0aXRsZSA9ICIyMDE4LzE5IikKcCAlKyUgdzIwICsgbGFicyh0aXRsZSA9ICIyMDIwLzIxIikKYGBgCgoKIyMjIENvbXBhcmluZyB0byBMb25nIFRlcm0gQXZlcmFnZXMKCkNvbXB1dGUgdGhlIGF2ZXJhZ2Ugc25vd2ZhbGwgZm9yIGVhY2ggZGF5IG9mIHRoZSB5ZWFyIGFuZCBtZXJnZSB0aGUKcmVzdWx0cyBmb3IgdGhlIGRheXMgaW4gYHcxOGAgaW50byBgdzE4YCB3aXRoIGEgYGxlZnRfam9pbmA6CgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQppY19hdmcgPC0gZ3JvdXBfYnkoaWNfZGF0YSwgbW9udGgsIGRheSkgfD4KICAgIHN1bW1hcml6ZShhdl9zbm93ID0gbWVhbihTTk9XLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgIGF2X3Nud2QgPSBtZWFuKFNOV0QsIG5hLnJtID0gVFJVRSkpIHw+CiAgICB1bmdyb3VwKCkKdzE4IDwtIGxlZnRfam9pbih3MTgsIGljX2F2ZywgYygibW9udGgiLCAiZGF5IikpCncyMCA8LSBsZWZ0X2pvaW4odzIwLCBpY19hdmcsIGMoIm1vbnRoIiwgImRheSIpKQpgYGAKCkRhaWx5IHNub3dmYWxsIGFuZCBzbm93IGNvdmVyIGZvciB0aGUgMjAxOC8xOSB3aW50ZXIgYWxvbmcgd2l0aAphdmVyYWdlcyBvdmVyIGFsbCB5ZWFycyBpbiB0aGUgcmVjb3JkOgoKYGBge3IsIGZpZy53aWR0aCA9IDl9CnAxIDwtIGdncGxvdCh3MTgsIGFlcyh4ID0gZGF0ZSkpICsKICAgIGdlb21fY29sKGFlcyh5ID0gU05PVykpICsKICAgIGdlb21fbGluZShhZXMoeSA9IGF2X3Nub3cpLCBjb2xvciA9ICJibHVlIikKcDIgPC0gZ2dwbG90KHcxOCwgYWVzKHggPSBkYXRlKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gU05XRCkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IGF2X3Nud2QpLCBjb2xvciA9ICJibHVlIikKKHAxICsgbGFicyh0aXRsZSA9ICIyMDE4LzE5IikpICsgcDIKKHAxICUrJSB3MjAgKyBsYWJzKHRpdGxlID0gIjIwMjAvMjEiKSkgKyAocDIgJSslIHcyMCkKYGBgCgpDdW11bGF0aXZlIHNub3dmYWxsIGZvciB0aGUgMjAxOC8xOSB3aW50ZXIgYWxvbmcgd2l0aCBhdmVyYWdlCmN1bXVsYXRpdmUgc25vd2ZhbGwgb3ZlciBhbGwgeWVhcnMgaW4gdGhlIHJlY29yZDoKCmBgYHtyfQpwIDwtIGdncGxvdCh3MTgsIGFlcyh4ID0gZGF0ZSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IGN1bXN1bShTTk9XKSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IGN1bXN1bShhdl9zbm93KSksIGNvbG9yID0gImJsdWUiKQpwICsgbGFicyh0aXRsZSA9ICIyMDE4LzE5IikKcCAlKyUgdzIwICsgbGFicyh0aXRsZSA9ICIyMDIwLzIxIikKYGBgCgoKIyMjIEEgQ29tYmluZWQgR3JhcGgKCmBgYHtyfQp3MThkYXkgPC0gdHJhbnNtdXRlKHcxOCwgZGF0ZSwgc25vdyA9IFNOT1csCiAgICAgICAgICAgICAgICAgICAgYmFzZSA9ICIyMDE4LzE5IiwKICAgICAgICAgICAgICAgICAgICB0eXBlID0gIkRhaWx5IFNub3dmYWxsIikKbnJtZGF5IDwtIHRyYW5zbXV0ZSh3MTgsIGRhdGUsIHNub3cgPSBhdl9zbm93LAogICAgICAgICAgICAgICAgICAgIGJhc2UgPSAiTm9ybWFsIiwKICAgICAgICAgICAgICAgICAgICB0eXBlID0gIkRhaWx5IFNub3dmYWxsIikKdzE4Y3VtIDwtIHRyYW5zbXV0ZSh3MThkYXksIGRhdGUsIHNub3cgPSBjdW1zdW0oc25vdyksCiAgICAgICAgICAgICAgICAgICAgYmFzZSA9ICIyMDE4LzE5IiwKICAgICAgICAgICAgICAgICAgICB0eXBlID0gIkN1bXVsYXRpdmUgU25vd2ZhbGwiKQpucm1jdW0gPC0gdHJhbnNtdXRlKG5ybWRheSwgZGF0ZSwgc25vdyA9IGN1bXN1bShzbm93KSwKICAgICAgICAgICAgICAgICAgICBiYXNlID0gIk5vcm1hbCIsCiAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJDdW11bGF0aXZlIFNub3dmYWxsIikKYGBgCgpgYGB7ciwgZmlnLmhlaWdodCA9IDh9CmdncGxvdCh3MTgsIGFlcyh4ID0gZGF0ZSwgeSA9IHNub3csIGNvbG9yID0gYmFzZSkpICsKICAgIGdlb21fbGluZShkYXRhID0gdzE4Y3VtKSArCiAgICBnZW9tX2xpbmUoZGF0YSA9IG5ybWN1bSkgKwogICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gZGF0ZSwgeGVuZCA9IGRhdGUsIHkgPSAwLCB5ZW5kID0gc25vdyksCiAgICAgICAgICAgICAgICAgc2l6ZSA9IDEsIGRhdGEgPSB3MThkYXkpICsKICAgIGdlb21fbGluZShkYXRhID0gbnJtZGF5KSArCiAgICBmYWNldF93cmFwKH4gdHlwZSwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgICB4bGFiKGVsZW1lbnRfYmxhbmsoKSkgKyB5bGFiKCJTbm93ZmFsbCAoaW5jaGVzKSIpICsKICAgIGdndGl0bGUoIjIwMTgvMTkgU25vd2ZhbGwgaW4gSW93YSBDaXR5IikgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIjIwMTgvMTkiID0gIm5hdnkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5vcm1hbCIgPSAiZm9yZXN0Z3JlZW4iKSkgKwogICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDEuNSkpKSArCiAgICB0aGVtZV9idygpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gLjUpLAogICAgICAgICAgYXNwZWN0LnJhdGlvID0gMC40KQpgYGAKCiMjIyBUb3RhbCBTbm93IEZhbGwgYnkgWWVhciBhbmQgTW9udGgKCkl0IGlzIGhlbHBmdWwgdG8gZGVmaW5lIGEgdmFyaWFibGUgdGhhdCBzaGlmdHMgdGhlIHllYXIgdG8gaW5jbHVkZSBhCmZ1bGwgd2ludGVyLCBub3QgdGhlIGVuZCBvZiBvbmUgYW5kIHRoZSBzdGFydCBvZiB0aGUgbmV4dC4KCldoYXQgbW9udGggc2hvdWxkIGJlIHN0YXJ0IHdpdGg/IEhlcmUgYXJlIHRoZSBjb3VudHMgZm9yIG1vbnRocyB3aXRoCnJlY29yZGVkIHNub3dmYWxsOgoKYGBge3J9CmNvdW50KGZpbHRlcihpY19kYXRhLCBTTk9XID4gMCksIG1vbnRoKQpgYGAKClRoZSBKdWx5IHNub3dmYWxsIHNlZW1zIG9kZDoKCmBgYHtyfQpmaWx0ZXIoaWNfZGF0YSwgU05PVyA+IDAsIG1vbnRoID09IDcpCmBgYAoKVGhpcyBpcyBwcm9iYWJseSBhIHJlY29yZGluZyBlcnJvci4KCkZvciBPY3RvYmVyLCB0aGUgeWVhcnMgd2l0aCAgcmVjb3JkZWQgcmVjb3JkZWQgc25vd2ZhbGwgYXJlIHNob3duIGJ5CgpgYGB7cn0KY291bnQoZmlsdGVyKGljX2RhdGEsIFNOT1cgPiAwLCBtb250aCA9PSAxMCksIHllYXIpCmBgYAoKSXQgc2VlbXMgcmVhc29uYWJsZSB0byBzdGFydCB0aGUgX3dpbnRlciB5ZWFyXyBpbiBPY3RvYmVyOgoKYGBge3J9CmljX2RhdGEgPC0gbXV0YXRlKGljX2RhdGEsIHd5ZWFyID0gaWZlbHNlKG1vbnRoID49IDEwLCB5ZWFyLCB5ZWFyIC0gMSkpCmBgYAoKVGhlIHllYXJseSB0b3RhbCBzbm93ZmFsbCB2YWx1ZXM6CgpgYGB7cn0KdG90IDwtIGdyb3VwX2J5KGljX2RhdGEsIHd5ZWFyKSB8PgogICAgc3VtbWFyaXplKHRzbm93ID0gc3VtKFNOT1csIG5hLnJtID0gVFJVRSkpCnRvdDE4IDwtIGZpbHRlcih0b3QsIHd5ZWFyID09IDIwMTgpCmdncGxvdCh0b3QsIGFlcyh4ID0gd3llYXIsIHkgPSB0c25vdykpICsKICAgIGdlb21fbGluZSgpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IHRvdDE4LCBjb2xvciA9ICJyZWQiKSArCiAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHRvdDE4LCBsYWJlbCA9ICIyMDE4LzE5IiwgY29sb3IgPSAicmVkIikKYGBgCgpTbyAyMDE4LzE5IGlzIG5vdCBhIHJlY29yZCwgYnV0IHN0aWxsIHByZXR0eSBoaWdoOgoKYGBge3J9CnRvdDEwMCA8LSBtdXRhdGUoZmlsdGVyKHRvdCwgd3llYXIgPj0gMTkxOCksIHJhbmsgPSBtaW5fcmFuayhkZXNjKHRzbm93KSkpCmZpbHRlcih0b3QxMDAsIHd5ZWFyID09IDIwMTgpCmBgYAoKV2hhdCBhcmUgdGhlIG1vbnRocyB3aXRoIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgdG90YWwgc25vd2ZhbGw/CgpgYGB7cn0KbXRvdCA8LSBncm91cF9ieShpY19kYXRhLCB5ZWFyLCB3eWVhciwgbW9udGgpIHw+CiAgICBzdW1tYXJpemUobXNub3cgPSBzdW0oU05PVywgbmEucm0gPSBUUlVFKSkgfD4KICAgIHVuZ3JvdXAoKQpncm91cF9ieShtdG90LCBtb250aCkgfD4gc3VtbWFyaXplKGF2X21zbm93ID0gbWVhbihtc25vdykpCmBgYAoKRm9yIDIwMTgvMTk6CgpgYGB7cn0KZmlsdGVyKG10b3QsIHd5ZWFyID09IDIwMTgpCmBgYApGb3IgMjAyMC8yMToKCmBgYHtyfQpmaWx0ZXIobXRvdCwgd3llYXIgPT0gMjAyMCkKYGBgCgoKIyMgQSBEYXRhIFByb2Nlc3NpbmcgRnVuY3Rpb25zCgpJZiB5b3Ugd2FudCB0byBsb29rIGF0IGRhdGEgZm9yIGFub3RoZXIgY2l0eSB5b3Ugd291bGQgbmVlZCB0byBnbwp0aHJvdWdoIHRoZSBzYW1lIHN0ZXBzIHN0YXJ0aW5nIHdpdGggYSBuZXcgcmF3IGRhdGEgZnJhbWUuCgpEZWZpbmluZyBhIGZ1bmN0aW9uIHRoYXQgZW5jYXBzdWxhdGVzIHRoZSBzdGVwcyBtYWtlcyB0aGlzIGVhc3k6Cgo8IS0tICMjIG5vbGludCBzdGFydDogb2JqZWN0X3VzYWdlIC0tPgpgYGB7cn0KcHJvY2Vzc0RhdGEgPC0gZnVuY3Rpb24oZGF0YV9yYXcpIHsKICAgICMjIHNlbGVjdCBqdXN0IHRoZSBjb2x1bW5zIHdlIG5lZWQ6CiAgICBkYXRhIDwtIHNlbGVjdChkYXRhX3JhdywgeWVhciwgbW9udGgsIGVsZW1lbnQsIHN0YXJ0c193aXRoKCJWQUxVRSIpKQoKICAgICMjIHJlc2hhcGUgZnJvbSAodmVyeSkgd2lkZSB0byAodG9vKSBsb25nCiAgICBkYXRhIDwtIHBpdm90X2xvbmdlcihkYXRhLCBWQUxVRTEgOiBWQUxVRTMxLAogICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiZGF5IiwgdmFsdWVzX3RvID0gInZhbHVlIikKICAgICMjIGV4dHJhY3QgdGhlIGRheSBhcyBhIG51bWJlcjoKICAgIGRhdGEgPC0gbXV0YXRlKGRhdGEsIGRheSA9IHJlYWRyOjpwYXJzZV9udW1iZXIoZGF5KSkKCiAgICAjIyByZXNoYXBlIGZyb20gdG9vIGxvbmcgdG8gdGlkeSB3aXRoIG9uZSByb3cgcGVyIGRheSwga2VlcGluZwogICAgIyMgb25seSB0aGUgcHJpbWFyeSB2YXJpYWJsZXM6CiAgICBjb3JldmFycyA8LSBjKCJUTUFYIiwgIlRNSU4iLCAiUFJDUCIsICJTTk9XIiwgIlNOV0QiKQogICAgZGF0YSA8LSBmaWx0ZXIoZGF0YSwgZWxlbWVudCAlaW4lIGNvcmV2YXJzKQogICAgZGF0YSA8LSBwaXZvdF93aWRlcihkYXRhLCBuYW1lc19mcm9tID0gImVsZW1lbnQiLCB2YWx1ZXNfZnJvbSA9ICJ2YWx1ZSIpCgogICAgIyMgYWRkIGEgJ2RhdGUnIHZhcmlhYmxlIGFuZCBnZXQgcmlkIG9mIGJvZ3VzIGRheXMKICAgIGRhdGEgPC0gbXV0YXRlKGRhdGEsIGRhdGUgPSBsdWJyaWRhdGU6Om1ha2VfZGF0ZSh5ZWFyLCBtb250aCwgZGF5KSkKICAgIGRhdGEgPC0gZmlsdGVyKGRhdGEsICEgaXMubmEoZGF0ZSkpCgogICAgIyMgbWFrZSB1bml0cyBtb3JlIHN0YW5kYXJkIChBbWVyaWNhbikKICAgIG1tMmluIDwtIGZ1bmN0aW9uKHgpIHggLyAyNS40CiAgICBDMkYgPC0gZnVuY3Rpb24oeCkgMzIgKyAxLjggKiB4CiAgICBkYXRhIDwtIHRyYW5zbXV0ZShkYXRhLCB5ZWFyLCBtb250aCwgZGF5LCBkYXRlLAogICAgICAgICAgICAgICAgICAgICAgUFJDUCA9IG1tMmluKFBSQ1AgLyAxMCksCiAgICAgICAgICAgICAgICAgICAgICBTTk9XID0gbW0yaW4oU05PVyksCiAgICAgICAgICAgICAgICAgICAgICBTTldEID0gbW0yaW4oU05XRCksCiAgICAgICAgICAgICAgICAgICAgICBUTUlOID0gQzJGKFRNSU4gLyAxMCksCiAgICAgICAgICAgICAgICAgICAgICBUTUFYID0gQzJGKFRNQVggLyAxMCkpCgogICAgIyMgYWRkIHdpbnRlciB5ZWFyIHZhcmlhYmxlICd3eWVhcicgc3RhcnRpbmcgaW4gT2N0b2JlcgogICAgZGF0YSA8LSBtdXRhdGUoZGF0YSwgd3llYXIgPSBpZmVsc2UobW9udGggPj0gMTAsIHllYXIsIHllYXIgLSAxKSkKCiAgICBkYXRhCn0KYGBgCjwhLS0gIyMgbm9saW50IGVuZCAtLT4KCkEgcXVpY2sgc2FuaXR5IGNoZWNrOgoKYGBge3J9CnN0b3BpZm5vdChpZGVudGljYWwoaWNfZGF0YSwgcHJvY2Vzc0RhdGEoaWNfZGF0YV9yYXcpKSkKYGBgCgpPZnRlbiBpdCBpcyBhIGdvb2QgaWRlYSB0byBpbmNsdWRlIGEgc2FuaXR5IGNoZWNrIGxpa2UgdGhpcyBpbiBhIGNvZGUKY2h1bmsgbWFya2VkIGFzIGBpbmNsdWRlID0gRkFMU0VgLgoKCiMjIE90aGVyIEV4cGxvcmF0aW9ucwoKYGBge3J9CnBpdm90X2xvbmdlcih3MTgsIFRNSU4gfCBUTUFYLCBuYW1lc190byA9ICJ3aGljaCIsIHZhbHVlc190byA9ICJ2YWx1ZSIpIHw+CiAgICBnZ3Bsb3QoYWVzKHggPSBkYXRlKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gdmFsdWUsIGNvbG9yID0gd2hpY2gpKQpgYGAKCgpgYGB7cn0Kd2QxOCA8LSBmaWx0ZXIoaWNfZGF0YSwgZGF0ZSA+PSAiMjAxOC0xMC0wMSIpCmF2cyA8LSBncm91cF9ieShpY19kYXRhLCBtb250aCwgZGF5KSB8PgogICAgc3VtbWFyaXplKGF2c25vdyA9IG1lYW4oU05PVywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICBhdnNud2QgPSBtZWFuKFNOV0QsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgYXZ0bWluID0gbWVhbihUTUlOLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgIGF2dG1heCA9IG1lYW4oVE1BWCwgbmEucm0gPSBUUlVFKSkgfD4KICAgIHVuZ3JvdXAoKQp3ZDE4IDwtIGxlZnRfam9pbih3ZDE4LCBhdnMpCgpnZ3Bsb3QoZmlsdGVyKHcxOCwgISBpcy5uYShTTldEKSkpICsKICAgIGdlb21fbGluZShhZXMoeCA9IGRhdGUsIHkgPSBTTldEKSkgKwogICAgZ2VvbV9saW5lKGFlcyh4ID0gZGF0ZSwgeSA9IGF2X3Nud2QpLCBjb2wgPSAiYmx1ZSIpCmBgYAoKCmBgYHtyfQpwbG90KFNOT1cgfiBkYXRlLCBkYXRhID0gd2QxOCwgdHlwZSA9ICJoIikKbGluZXMod2QxOCRkYXRlLCB3ZDE4JGF2c25vdywgY29sID0gInJlZCIpCmxpbmVzKHNtb290aC5zcGxpbmUod2QxOCRkYXRlLCB3ZDE4JGF2c25vdyksIGNvbCA9ICJibHVlIikKCnBsb3QoU05XRCB+IGRhdGUsIGRhdGEgPSBmaWxsKHdkMTgsIFNOV0QpLCB0eXBlID0gImwiKQpsaW5lcyh3ZDE4JGRhdGUsIHdkMTgkYXZzbndkLCBjb2wgPSAicmVkIikKCnBsb3QoY3Vtc3VtKGlmZWxzZShpcy5uYShTTk9XKSwgMCwgU05PVykpIH4gZGF0ZSwgZGF0YSA9IHdkMTgsIHR5cGUgPSAibCIpCmxpbmVzKHdkMTgkZGF0ZSwgY3Vtc3VtKHdkMTgkYXZzbm93KSwgY29sID0gInJlZCIpCmBgYAoKYGBge3J9CnRobSA8LSB0aGVtZV9idygpICsKICAgIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpCgpnZ3Bsb3Qod2QxOCkgKwogICAgZ2VvbV9jb2woYWVzKHggPSBkYXRlLCB5ID0gU05PVykpICsKICAgIGdlb21fbGluZShhZXMoeCA9IGRhdGUsIHkgPSBhdnNub3cpLCBjb2wgPSAicmVkIikgKwogICAgZ2VvbV9zbW9vdGgoYWVzKHggPSBkYXRlLCB5ID0gYXZzbm93KSwgc2UgPSBGQUxTRSkgKwogICAgdGhtCgpnZ3Bsb3QoZmlsbCh3ZDE4LCBTTldEKSkgKwogICAgZ2VvbV9saW5lKGFlcyh4ID0gZGF0ZSwgeSA9IFNOV0QpKSArCiAgICBnZW9tX2xpbmUoYWVzKHggPSBkYXRlLCB5ID0gYXZzbndkKSwgY29sID0gInJlZCIpICsKICAgIGdlb21fc21vb3RoKGFlcyh4ID0gZGF0ZSwgeSA9IGF2c253ZCksIHNlID0gRkFMU0UpICsKICAgIHRobQoKZ2dwbG90KHdkMTgpICsKICAgIGdlb21fbGluZShhZXMoeCA9IGRhdGUsIHkgPSBjdW1zdW0oaWZlbHNlKGlzLm5hKFNOT1cpLCAwLCBTTk9XKSkpKSArCiAgICBnZW9tX2xpbmUoYWVzKHggPSBkYXRlLCB5ID0gY3Vtc3VtKHdkMTgkYXZzbm93KSksIGNvbCA9ICJyZWQiKSArCiAgICB0aG0KYGBgCgpgYGB7cn0KZ2dwbG90KGZpbHRlcihpY19kYXRhLCB5ZWFyID09IDIwMTkpLCBhZXMoeCA9IGRhdGUpKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBUTUFYLCBjb2xvciA9ICJUTUFYIikpICsKICAgIGdlb21fbGluZShhZXMoeSA9IFRNSU4sIGNvbG9yID0gIlRNSU4iKSkgKwogICAgdGhtCmBgYAoKYGBge3J9CmQxMyA8LSBmaWx0ZXIoaWNfZGF0YSwgd3llYXIgPj0gMjAxMywgbW9udGggPD0gNCB8IG1vbnRoID49IDEwKQpkMTMgPC0gbXV0YXRlKGQxMywgd2RheSA9IGFzLm51bWVyaWMoZGF0ZSAtIGx1YnJpZGF0ZTo6bWFrZV9kYXRlKHd5ZWFyLCAxMCwgMSkpLAogICAgICAgICAgICAgIFNOT1cgPSBpZmVsc2UoaXMubmEoU05PVyksIDAsIFNOT1cpKQpnZ3Bsb3QoZDEzKSArCiAgICBnZW9tX2NvbChhZXMoeCA9IHdkYXksIHkgPSBTTk9XKSwgd2lkdGggPSAzLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsKICAgIGZhY2V0X3dyYXAofiB3eWVhcikgKyB0aG0KZ2dwbG90KGQxMykgKyBnZW9tX2xpbmUoYWVzKHggPSB3ZGF5LCB5ID0gU05PVykpICsgZmFjZXRfd3JhcCh+IHd5ZWFyKSArIHRobQpgYGAKCmBgYHtyfQpkMTMgPC0gZ3JvdXBfYnkoZDEzLCB3eWVhcikgfD4KICAgIG11dGF0ZShzbm93X3RvdGFsID0gY3Vtc3VtKFNOT1cpKSB8PgogICAgdW5ncm91cCgpCmdncGxvdChkMTMpICsKICAgIGdlb21fbGluZShhZXMoeCA9IHdkYXksIHkgPSBzbm93X3RvdGFsLCBjb2xvciA9IGZhY3Rvcih3eWVhcikpKSArIHRobQpnZ3Bsb3QoZDEzKSArCiAgICBnZW9tX2xpbmUoYWVzKHggPSB3ZGF5LCB5ID0gc25vd190b3RhbCkpICsKICAgIGZhY2V0X3dyYXAofiB3eWVhcikgKyB0aG0KYGBgCgpgYGB7cn0KZDEzIDwtIGZpbGwoZDEzLCBTTldEKQpnZ3Bsb3QoZDEzKSArIGdlb21fbGluZShhZXMoeCA9IHdkYXksIHkgPSBTTldEKSkgKyBmYWNldF93cmFwKH4gd3llYXIpICsgdGhtCmBgYAoKYGBge3J9CncgPC0gZmlsdGVyKGljX2RhdGEsIHd5ZWFyID49IDIwMTMpCncgPC0gbXV0YXRlKHcsIHdkYXkgPSBkYXRlIC0gbHVicmlkYXRlOjptYWtlX2RhdGUod3llYXIsIDEwLCAxKSkKdyA8LSBmaWx0ZXIodywgd2RheSA8PSAxODApCncgPC0gbXV0YXRlKHcsIHdkYXkgPSBsdWJyaWRhdGU6Om1ha2VfZGF0ZSgyMDE4LCAxMCwgMSkgKyB3ZGF5KQpnZ3Bsb3QodykgKwogICAgZ2VvbV9saW5lKGFlcyh4ID0gd2RheSwgeSA9IFRNQVgpLCBjb2xvciA9ICJibHVlIikgKwogICAgZ2VvbV9saW5lKGFlcyh4ID0gd2RheSwgeSA9IFRNSU4pLCBjb2xvciA9ICJyZWQiKSArCiAgICBmYWNldF93cmFwKH4gd3llYXIpCgpgYGAKCi0gQmV0dGVyIHggYXhpcyBsYWJlbCBmb3Igd2ludGVyIGRheXMKLSBzZXQgdGhlbWUgZ2xvYmFsbHkKLSByZS1jcmVhdGUgb3JpZ2luYWwgcGxvdCBpbiBzb21lIHZhcmlhbnRzCi0gYmV0dGVyIGZhY2V0IGxhYmVscwo=